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
2 years ago
|
|
|
/* 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
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \ingroup dpdk
|
|
|
|
|
*
|
|
|
|
|
* @{
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \file
|
|
|
|
|
*
|
|
|
|
|
* \author Lukas Sismis <lukas.sismis@gmail.com>
|
|
|
|
|
*
|
|
|
|
|
* DPDK runmode
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "suricata-common.h"
|
|
|
|
|
#include "runmodes.h"
|
|
|
|
|
#include "runmode-dpdk.h"
|
|
|
|
|
#include "decode.h"
|
|
|
|
|
#include "source-dpdk.h"
|
|
|
|
|
#include "util-runmodes.h"
|
|
|
|
|
#include "util-byte.h"
|
|
|
|
|
#include "util-cpu.h"
|
|
|
|
|
#include "util-debug.h"
|
|
|
|
|
#include "util-device-private.h"
|
|
|
|
|
#include "util-dpdk.h"
|
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
2 years ago
|
|
|
#include "util-dpdk-bonding.h"
|
|
|
|
|
#include "util-dpdk-common.h"
|
|
|
|
|
#include "util-dpdk-i40e.h"
|
|
|
|
|
#include "util-dpdk-ice.h"
|
|
|
|
|
#include "util-dpdk-ixgbe.h"
|
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
2 years ago
|
|
|
#include "util-dpdk-rss.h"
|
|
|
|
|
#include "util-time.h"
|
|
|
|
|
#include "util-conf.h"
|
|
|
|
|
#include "suricata.h"
|
|
|
|
|
#include "util-affinity.h"
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_DPDK
|
|
|
|
|
|
|
|
|
|
// Calculates the closest multiple of y from x
|
|
|
|
|
#define ROUNDUP(x, y) ((((x) + ((y)-1)) / (y)) * (y))
|
|
|
|
|
|
|
|
|
|
/* Maximum DPDK EAL parameters count. */
|
|
|
|
|
#define EAL_ARGS 48
|
|
|
|
|
|
|
|
|
|
struct Arguments {
|
|
|
|
|
uint16_t capacity;
|
|
|
|
|
char **argv;
|
|
|
|
|
uint16_t argc;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static char *AllocArgument(size_t arg_len);
|
|
|
|
|
static char *AllocAndSetArgument(const char *arg);
|
|
|
|
|
static char *AllocAndSetOption(const char *arg);
|
|
|
|
|
|
|
|
|
|
static void ArgumentsInit(struct Arguments *args, uint16_t capacity);
|
|
|
|
|
static void ArgumentsCleanup(struct Arguments *args);
|
|
|
|
|
static void ArgumentsAdd(struct Arguments *args, char *value);
|
|
|
|
|
static void ArgumentsAddOptionAndArgument(struct Arguments *args, const char *opt, const char *arg);
|
|
|
|
|
static void InitEal(void);
|
|
|
|
|
|
|
|
|
|
static void ConfigSetIface(DPDKIfaceConfig *iconf, const char *entry_str);
|
|
|
|
|
static int ConfigSetThreads(DPDKIfaceConfig *iconf, const char *entry_str);
|
|
|
|
|
static int ConfigSetRxQueues(DPDKIfaceConfig *iconf, uint16_t nb_queues, uint16_t max_queues);
|
|
|
|
|
static int ConfigSetTxQueues(
|
|
|
|
|
DPDKIfaceConfig *iconf, uint16_t nb_queues, uint16_t max_queues, bool iface_sends_pkts);
|
|
|
|
|
static int ConfigSetMempoolSize(DPDKIfaceConfig *iconf, const char *entry_str);
|
|
|
|
|
static int ConfigSetMempoolCacheSize(DPDKIfaceConfig *iconf, const char *entry_str);
|
|
|
|
|
static int ConfigSetRxDescriptors(DPDKIfaceConfig *iconf, const char *entry_str, uint16_t max_desc);
|
|
|
|
|
static int ConfigSetTxDescriptors(
|
|
|
|
|
DPDKIfaceConfig *iconf, const char *entry_str, uint16_t max_desc, bool iface_sends_pkts);
|
|
|
|
|
static int ConfigSetMtu(DPDKIfaceConfig *iconf, intmax_t entry_int);
|
|
|
|
|
static bool ConfigSetPromiscuousMode(DPDKIfaceConfig *iconf, int entry_bool);
|
|
|
|
|
static bool ConfigSetMulticast(DPDKIfaceConfig *iconf, int entry_bool);
|
|
|
|
|
static int ConfigSetChecksumChecks(DPDKIfaceConfig *iconf, int entry_bool);
|
|
|
|
|
static int ConfigSetChecksumOffload(DPDKIfaceConfig *iconf, int entry_bool);
|
|
|
|
|
static int ConfigSetCopyIface(DPDKIfaceConfig *iconf, const char *entry_str);
|
|
|
|
|
static int ConfigSetCopyMode(DPDKIfaceConfig *iconf, const char *entry_str);
|
|
|
|
|
static int ConfigSetCopyIfaceSettings(DPDKIfaceConfig *iconf, const char *iface, const char *mode);
|
|
|
|
|
static void ConfigInit(DPDKIfaceConfig **iconf);
|
|
|
|
|
static int ConfigLoad(DPDKIfaceConfig *iconf, const char *iface);
|
|
|
|
|
static DPDKIfaceConfig *ConfigParse(const char *iface);
|
|
|
|
|
|
|
|
|
|
static void DeviceInitPortConf(const DPDKIfaceConfig *iconf,
|
|
|
|
|
const struct rte_eth_dev_info *dev_info, struct rte_eth_conf *port_conf);
|
|
|
|
|
static int DeviceConfigureQueues(DPDKIfaceConfig *iconf, const struct rte_eth_dev_info *dev_info,
|
|
|
|
|
const struct rte_eth_conf *port_conf);
|
|
|
|
|
static int DeviceValidateOutIfaceConfig(DPDKIfaceConfig *iconf);
|
|
|
|
|
static int DeviceConfigureIPS(DPDKIfaceConfig *iconf);
|
|
|
|
|
static int DeviceConfigure(DPDKIfaceConfig *iconf);
|
|
|
|
|
static void *ParseDpdkConfigAndConfigureDevice(const char *iface);
|
|
|
|
|
static void DPDKDerefConfig(void *conf);
|
|
|
|
|
|
|
|
|
|
#define DPDK_CONFIG_DEFAULT_THREADS "auto"
|
|
|
|
|
#define DPDK_CONFIG_DEFAULT_INTERRUPT_MODE false
|
|
|
|
|
#define DPDK_CONFIG_DEFAULT_MEMPOOL_SIZE "auto"
|
|
|
|
|
#define DPDK_CONFIG_DEFAULT_MEMPOOL_CACHE_SIZE "auto"
|
|
|
|
|
#define DPDK_CONFIG_DEFAULT_RX_DESCRIPTORS "auto"
|
|
|
|
|
#define DPDK_CONFIG_DEFAULT_TX_DESCRIPTORS "auto"
|
|
|
|
|
#define DPDK_CONFIG_DEFAULT_RSS_HASH_FUNCTIONS RTE_ETH_RSS_IP
|
|
|
|
|
#define DPDK_CONFIG_DEFAULT_MTU 1500
|
|
|
|
|
#define DPDK_CONFIG_DEFAULT_PROMISCUOUS_MODE 1
|
|
|
|
|
#define DPDK_CONFIG_DEFAULT_MULTICAST_MODE 1
|
|
|
|
|
#define DPDK_CONFIG_DEFAULT_CHECKSUM_VALIDATION 1
|
|
|
|
|
#define DPDK_CONFIG_DEFAULT_CHECKSUM_VALIDATION_OFFLOAD 1
|
|
|
|
|
#define DPDK_CONFIG_DEFAULT_VLAN_STRIP 0
|
|
|
|
|
#define DPDK_CONFIG_DEFAULT_LINKUP_TIMEOUT 0
|
|
|
|
|
#define DPDK_CONFIG_DEFAULT_COPY_MODE "none"
|
|
|
|
|
#define DPDK_CONFIG_DEFAULT_COPY_INTERFACE "none"
|
|
|
|
|
|
|
|
|
|
DPDKIfaceConfigAttributes dpdk_yaml = {
|
|
|
|
|
.threads = "threads",
|
|
|
|
|
.irq_mode = "interrupt-mode",
|
|
|
|
|
.promisc = "promisc",
|
|
|
|
|
.multicast = "multicast",
|
|
|
|
|
.checksum_checks = "checksum-checks",
|
|
|
|
|
.checksum_checks_offload = "checksum-checks-offload",
|
|
|
|
|
.mtu = "mtu",
|
|
|
|
|
.vlan_strip_offload = "vlan-strip-offload",
|
|
|
|
|
.rss_hf = "rss-hash-functions",
|
|
|
|
|
.linkup_timeout = "linkup-timeout",
|
|
|
|
|
.mempool_size = "mempool-size",
|
|
|
|
|
.mempool_cache_size = "mempool-cache-size",
|
|
|
|
|
.rx_descriptors = "rx-descriptors",
|
|
|
|
|
.tx_descriptors = "tx-descriptors",
|
|
|
|
|
.copy_mode = "copy-mode",
|
|
|
|
|
.copy_iface = "copy-iface",
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \brief Input is a number of which we want to find the greatest divisor up to max_num (inclusive).
|
|
|
|
|
* The divisor is returned.
|
|
|
|
|
*/
|
|
|
|
|
static uint32_t GreatestDivisorUpTo(uint32_t num, uint32_t max_num)
|
|
|
|
|
{
|
|
|
|
|
for (uint32_t i = max_num; i >= 2; i--) {
|
|
|
|
|
if (num % i == 0) {
|
|
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \brief Input is a number of which we want to find the greatest power of 2 up to num. The power of
|
|
|
|
|
* 2 is returned or 0 if no valid power of 2 is found.
|
|
|
|
|
*/
|
|
|
|
|
static uint64_t GreatestPowOf2UpTo(uint64_t num)
|
|
|
|
|
{
|
|
|
|
|
if (num == 0) {
|
|
|
|
|
return 0; // No power of 2 exists for 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Bit manipulation to isolate the highest set bit
|
|
|
|
|
num |= (num >> 1);
|
|
|
|
|
num |= (num >> 2);
|
|
|
|
|
num |= (num >> 4);
|
|
|
|
|
num |= (num >> 8);
|
|
|
|
|
num |= (num >> 16);
|
|
|
|
|
num |= (num >> 32);
|
|
|
|
|
num = num - (num >> 1);
|
|
|
|
|
|
|
|
|
|
return num;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *AllocArgument(size_t arg_len)
|
|
|
|
|
{
|
|
|
|
|
SCEnter();
|
|
|
|
|
char *ptr;
|
|
|
|
|
|
|
|
|
|
arg_len += 1; // null character
|
|
|
|
|
ptr = (char *)SCCalloc(arg_len, sizeof(char));
|
|
|
|
|
if (ptr == NULL)
|
|
|
|
|
FatalError("Could not allocate memory for an argument");
|
|
|
|
|
|
|
|
|
|
SCReturnPtr(ptr, "char *");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Allocates space for length of the given string and then copies contents
|
|
|
|
|
* @param arg String to set to the newly allocated space
|
|
|
|
|
* @return memory address if no error otherwise NULL (with errno set)
|
|
|
|
|
*/
|
|
|
|
|
static char *AllocAndSetArgument(const char *arg)
|
|
|
|
|
{
|
|
|
|
|
SCEnter();
|
|
|
|
|
if (arg == NULL)
|
|
|
|
|
FatalError("Passed argument is NULL in DPDK config initialization");
|
|
|
|
|
|
|
|
|
|
char *ptr;
|
|
|
|
|
size_t arg_len = strlen(arg);
|
|
|
|
|
|
|
|
|
|
ptr = AllocArgument(arg_len);
|
|
|
|
|
strlcpy(ptr, arg, arg_len + 1);
|
|
|
|
|
SCReturnPtr(ptr, "char *");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *AllocAndSetOption(const char *arg)
|
|
|
|
|
{
|
|
|
|
|
SCEnter();
|
|
|
|
|
if (arg == NULL)
|
|
|
|
|
FatalError("Passed option is NULL in DPDK config initialization");
|
|
|
|
|
|
|
|
|
|
char *ptr = NULL;
|
|
|
|
|
size_t arg_len = strlen(arg);
|
|
|
|
|
uint8_t is_long_arg = arg_len > 1;
|
|
|
|
|
const char *dash_prefix = is_long_arg ? "--" : "-";
|
|
|
|
|
size_t full_len = arg_len + strlen(dash_prefix);
|
|
|
|
|
|
|
|
|
|
ptr = AllocArgument(full_len);
|
|
|
|
|
strlcpy(ptr, dash_prefix, full_len);
|
|
|
|
|
strlcat(ptr, arg, full_len + 1);
|
|
|
|
|
SCReturnPtr(ptr, "char *");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ArgumentsInit(struct Arguments *args, uint16_t capacity)
|
|
|
|
|
{
|
|
|
|
|
SCEnter();
|
|
|
|
|
args->argv = SCCalloc(capacity, sizeof(*args->argv)); // alloc array of pointers
|
|
|
|
|
if (args->argv == NULL)
|
|
|
|
|
FatalError("Could not allocate memory for Arguments structure");
|
|
|
|
|
|
|
|
|
|
args->capacity = capacity;
|
|
|
|
|
args->argc = 0;
|
|
|
|
|
SCReturn;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ArgumentsCleanup(struct Arguments *args)
|
|
|
|
|
{
|
|
|
|
|
SCEnter();
|
|
|
|
|
for (int i = 0; i < args->argc; i++) {
|
|
|
|
|
if (args->argv[i] != NULL) {
|
|
|
|
|
SCFree(args->argv[i]);
|
|
|
|
|
args->argv[i] = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SCFree(args->argv);
|
|
|
|
|
args->argv = NULL;
|
|
|
|
|
args->argc = 0;
|
|
|
|
|
args->capacity = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ArgumentsAdd(struct Arguments *args, char *value)
|
|
|
|
|
{
|
|
|
|
|
SCEnter();
|
|
|
|
|
if (args->argc + 1 > args->capacity)
|
|
|
|
|
FatalError("No capacity for more arguments (Max: %" PRIu32 ")", EAL_ARGS);
|
|
|
|
|
|
|
|
|
|
args->argv[args->argc++] = value;
|
|
|
|
|
SCReturn;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ArgumentsAddOptionAndArgument(struct Arguments *args, const char *opt, const char *arg)
|
|
|
|
|
{
|
|
|
|
|
SCEnter();
|
|
|
|
|
char *option;
|
|
|
|
|
char *argument;
|
|
|
|
|
|
|
|
|
|
option = AllocAndSetOption(opt);
|
|
|
|
|
ArgumentsAdd(args, option);
|
|
|
|
|
|
|
|
|
|
// Empty argument could mean option only (e.g. --no-huge)
|
|
|
|
|
if (arg == NULL || arg[0] == '\0')
|
|
|
|
|
SCReturn;
|
|
|
|
|
|
|
|
|
|
argument = AllocAndSetArgument(arg);
|
|
|
|
|
ArgumentsAdd(args, argument);
|
|
|
|
|
SCReturn;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void InitEal(void)
|
|
|
|
|
{
|
|
|
|
|
SCEnter();
|
|
|
|
|
int retval;
|
|
|
|
|
SCConfNode *param;
|
|
|
|
|
const SCConfNode *eal_params = SCConfGetNode("dpdk.eal-params");
|
|
|
|
|
struct Arguments args;
|
|
|
|
|
char **eal_argv;
|
|
|
|
|
|
|
|
|
|
if (eal_params == NULL) {
|
|
|
|
|
FatalError("DPDK EAL parameters not found in the config");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ArgumentsInit(&args, EAL_ARGS);
|
|
|
|
|
ArgumentsAdd(&args, AllocAndSetArgument("suricata"));
|
|
|
|
|
|
|
|
|
|
TAILQ_FOREACH (param, &eal_params->head, next) {
|
|
|
|
|
if (SCConfNodeIsSequence(param)) {
|
|
|
|
|
const char *key = param->name;
|
|
|
|
|
SCConfNode *val;
|
|
|
|
|
TAILQ_FOREACH (val, ¶m->head, next) {
|
|
|
|
|
ArgumentsAddOptionAndArgument(&args, key, (const char *)val->val);
|
|
|
|
|
}
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
ArgumentsAddOptionAndArgument(&args, param->name, param->val);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// creating a shallow copy for cleanup because rte_eal_init changes array contents
|
|
|
|
|
eal_argv = SCCalloc(args.argc, sizeof(*args.argv));
|
|
|
|
|
if (eal_argv == NULL) {
|
|
|
|
|
FatalError("Failed to allocate memory for the array of DPDK EAL arguments");
|
|
|
|
|
}
|
|
|
|
|
memcpy(eal_argv, args.argv, args.argc * sizeof(*args.argv));
|
|
|
|
|
|
|
|
|
|
rte_log_set_global_level(RTE_LOG_WARNING);
|
|
|
|
|
retval = rte_eal_init(args.argc, eal_argv);
|
|
|
|
|
|
|
|
|
|
ArgumentsCleanup(&args);
|
|
|
|
|
SCFree(eal_argv);
|
|
|
|
|
|
|
|
|
|
if (retval < 0) { // retval bound to the result of rte_eal_init
|
|
|
|
|
FatalError("DPDK EAL initialization error: %s", rte_strerror(-retval));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void DPDKDerefConfig(void *conf)
|
|
|
|
|
{
|
|
|
|
|
SCEnter();
|
|
|
|
|
DPDKIfaceConfig *iconf = (DPDKIfaceConfig *)conf;
|
|
|
|
|
|
|
|
|
|
if (SC_ATOMIC_SUB(iconf->ref, 1) == 1) {
|
|
|
|
|
DPDKDeviceResourcesDeinit(&iconf->pkt_mempools);
|
|
|
|
|
SCFree(iconf);
|
|
|
|
|
}
|
|
|
|
|
SCReturn;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ConfigInit(DPDKIfaceConfig **iconf)
|
|
|
|
|
{
|
|
|
|
|
SCEnter();
|
|
|
|
|
DPDKIfaceConfig *ptr = NULL;
|
|
|
|
|
ptr = SCCalloc(1, sizeof(DPDKIfaceConfig));
|
|
|
|
|
if (ptr == NULL)
|
|
|
|
|
FatalError("Could not allocate memory for DPDKIfaceConfig");
|
|
|
|
|
|
|
|
|
|
ptr->out_port_id = UINT16_MAX; // make sure no port is set
|
|
|
|
|
SC_ATOMIC_INIT(ptr->ref);
|
|
|
|
|
(void)SC_ATOMIC_ADD(ptr->ref, 1);
|
|
|
|
|
ptr->DerefFunc = DPDKDerefConfig;
|
|
|
|
|
ptr->flags = 0;
|
|
|
|
|
|
|
|
|
|
*iconf = ptr;
|
|
|
|
|
SCReturn;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ConfigSetIface(DPDKIfaceConfig *iconf, const char *entry_str)
|
|
|
|
|
{
|
|
|
|
|
SCEnter();
|
|
|
|
|
int retval;
|
|
|
|
|
|
|
|
|
|
if (entry_str == NULL || entry_str[0] == '\0')
|
|
|
|
|
FatalError("Interface name in DPDK config is NULL or empty");
|
|
|
|
|
|
|
|
|
|
retval = rte_eth_dev_get_port_by_name(entry_str, &iconf->port_id);
|
|
|
|
|
if (retval < 0)
|
|
|
|
|
FatalError("%s: interface not found: %s", entry_str, rte_strerror(-retval));
|
|
|
|
|
|
|
|
|
|
strlcpy(iconf->iface, entry_str, sizeof(iconf->iface));
|
|
|
|
|
SCReturn;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ConfigSetThreads(DPDKIfaceConfig *iconf, const char *entry_str)
|
|
|
|
|
{
|
|
|
|
|
SCEnter();
|
|
|
|
|
static uint16_t remaining_auto_cpus = UINT16_MAX; // uninitialized
|
|
|
|
|
if (!threading_set_cpu_affinity) {
|
|
|
|
|
SCLogError("DPDK runmode requires configured thread affinity");
|
|
|
|
|
SCReturnInt(-EINVAL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool wtaf_periface = true;
|
|
|
|
|
ThreadsAffinityType *wtaf = GetAffinityTypeForNameAndIface("worker-cpu-set", iconf->iface);
|
|
|
|
|
if (wtaf == NULL) {
|
|
|
|
|
wtaf_periface = false;
|
|
|
|
|
wtaf = GetAffinityTypeForNameAndIface("worker-cpu-set", NULL); // mandatory
|
|
|
|
|
if (wtaf == NULL) {
|
|
|
|
|
SCLogError("Specify worker-cpu-set list in the threading section");
|
|
|
|
|
SCReturnInt(-EINVAL);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ThreadsAffinityType *mtaf = GetAffinityTypeForNameAndIface("management-cpu-set", NULL);
|
|
|
|
|
if (mtaf == NULL) {
|
|
|
|
|
SCLogError("Specify management-cpu-set list in the threading section");
|
|
|
|
|
SCReturnInt(-EINVAL);
|
|
|
|
|
}
|
|
|
|
|
uint16_t sched_cpus = UtilAffinityGetAffinedCPUNum(wtaf);
|
|
|
|
|
if (sched_cpus == UtilCpuGetNumProcessorsOnline()) {
|
|
|
|
|
SCLogWarning(
|
|
|
|
|
"\"all\" specified in worker CPU cores affinity, excluding management threads");
|
|
|
|
|
UtilAffinityCpusExclude(wtaf, mtaf);
|
|
|
|
|
sched_cpus = UtilAffinityGetAffinedCPUNum(wtaf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (sched_cpus == 0) {
|
|
|
|
|
SCLogError("No worker CPU cores with configured affinity were configured");
|
|
|
|
|
SCReturnInt(-EINVAL);
|
|
|
|
|
} else if (UtilAffinityCpusOverlap(wtaf, mtaf) != 0) {
|
|
|
|
|
SCLogWarning("Worker threads should not overlap with management threads in the CPU core "
|
|
|
|
|
"affinity configuration");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *active_runmode = RunmodeGetActive();
|
|
|
|
|
if (active_runmode && !strcmp("single", active_runmode)) {
|
|
|
|
|
iconf->threads = 1;
|
|
|
|
|
SCReturnInt(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (entry_str == NULL) {
|
|
|
|
|
SCLogError("Number of threads for interface \"%s\" not specified", iconf->iface);
|
|
|
|
|
SCReturnInt(-EINVAL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (strcmp(entry_str, "auto") == 0) {
|
|
|
|
|
if (wtaf_periface) {
|
|
|
|
|
iconf->threads = (uint16_t)sched_cpus;
|
|
|
|
|
SCLogConfig("%s: auto-assigned %u threads", iconf->iface, iconf->threads);
|
|
|
|
|
SCReturnInt(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint16_t live_dev_count = (uint16_t)LiveGetDeviceCountWithoutAssignedThreading();
|
|
|
|
|
if (live_dev_count == 0) {
|
|
|
|
|
SCLogError("No live devices found, cannot auto-assign threads");
|
|
|
|
|
SCReturnInt(-EINVAL);
|
|
|
|
|
}
|
|
|
|
|
iconf->threads = sched_cpus / live_dev_count;
|
|
|
|
|
if (iconf->threads == 0) {
|
|
|
|
|
SCLogError("Not enough worker CPU cores with affinity were configured");
|
|
|
|
|
SCReturnInt(-ERANGE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (remaining_auto_cpus == UINT16_MAX) {
|
|
|
|
|
// first time auto-assignment
|
|
|
|
|
remaining_auto_cpus = sched_cpus % live_dev_count;
|
|
|
|
|
if (remaining_auto_cpus > 0) {
|
|
|
|
|
iconf->threads++;
|
|
|
|
|
remaining_auto_cpus--;
|
|
|
|
|
}
|
|
|
|
|
} else if (remaining_auto_cpus > 0) {
|
|
|
|
|
iconf->threads++;
|
|
|
|
|
remaining_auto_cpus--;
|
|
|
|
|
}
|
|
|
|
|
SCLogConfig("%s: auto-assigned %u threads", iconf->iface, iconf->threads);
|
|
|
|
|
SCReturnInt(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (StringParseUint16(&iconf->threads, 10, 0, entry_str) < 0) {
|
|
|
|
|
SCLogError("Threads entry for interface %s contain non-numerical characters - \"%s\"",
|
|
|
|
|
iconf->iface, entry_str);
|
|
|
|
|
SCReturnInt(-EINVAL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (iconf->threads <= 0) {
|
|
|
|
|
SCLogError("%s: positive number of threads required", iconf->iface);
|
|
|
|
|
SCReturnInt(-ERANGE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SCReturnInt(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool ConfigSetInterruptMode(DPDKIfaceConfig *iconf, bool enable)
|
|
|
|
|
{
|
|
|
|
|
SCEnter();
|
|
|
|
|
if (enable)
|
|
|
|
|
iconf->flags |= DPDK_IRQ_MODE;
|
|
|
|
|
|
|
|
|
|
SCReturnBool(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ConfigSetRxQueues(DPDKIfaceConfig *iconf, uint16_t nb_queues, uint16_t max_queues)
|
|
|
|
|
{
|
|
|
|
|
SCEnter();
|
|
|
|
|
if (nb_queues == 0) {
|
|
|
|
|
SCLogInfo("%s: positive number of RX queues is required", iconf->iface);
|
|
|
|
|
SCReturnInt(-ERANGE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (nb_queues > max_queues) {
|
|
|
|
|
SCLogInfo("%s: number of RX queues cannot exceed %" PRIu16, iconf->iface, max_queues);
|
|
|
|
|
SCReturnInt(-ERANGE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
iconf->nb_rx_queues = nb_queues;
|
|
|
|
|
SCReturnInt(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool ConfigIfaceSendsPkts(const char *mode)
|
|
|
|
|
{
|
|
|
|
|
SCEnter();
|
|
|
|
|
if (strcmp(mode, "ips") == 0 || strcmp(mode, "tap") == 0) {
|
|
|
|
|
SCReturnBool(true);
|
|
|
|
|
}
|
|
|
|
|
SCReturnBool(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ConfigSetTxQueues(
|
|
|
|
|
DPDKIfaceConfig *iconf, uint16_t nb_queues, uint16_t max_queues, bool iface_sends_pkts)
|
|
|
|
|
{
|
|
|
|
|
SCEnter();
|
|
|
|
|
if (nb_queues == 0 && iface_sends_pkts) {
|
|
|
|
|
SCLogInfo("%s: positive number of TX queues is required", iconf->iface);
|
|
|
|
|
SCReturnInt(-ERANGE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (nb_queues > max_queues) {
|
|
|
|
|
SCLogInfo("%s: number of TX queues cannot exceed %" PRIu16, iconf->iface, max_queues);
|
|
|
|
|
SCReturnInt(-ERANGE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
iconf->nb_tx_queues = nb_queues;
|
|
|
|
|
SCReturnInt(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static uint32_t MempoolSizeCalculate(
|
|
|
|
|
uint32_t rx_queues, uint32_t rx_desc, uint32_t tx_queues, uint32_t tx_desc)
|
|
|
|
|
{
|
|
|
|
|
uint32_t sz = rx_queues * rx_desc + tx_queues * tx_desc;
|
|
|
|
|
if (!tx_queues || !tx_desc)
|
|
|
|
|
sz *= 2; // double to have enough space for RX descriptors
|
|
|
|
|
|
|
|
|
|
return sz;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ConfigSetMempoolSize(DPDKIfaceConfig *iconf, const char *entry_str)
|
|
|
|
|
{
|
|
|
|
|
SCEnter();
|
|
|
|
|
if (entry_str == NULL || entry_str[0] == '\0' || strcmp(entry_str, "auto") == 0) {
|
|
|
|
|
// calculate the mempool size based on the number of:
|
|
|
|
|
// - RX / TX queues
|
|
|
|
|
// - RX / TX descriptors
|
|
|
|
|
bool err = false;
|
|
|
|
|
if (iconf->nb_rx_queues == 0) {
|
|
|
|
|
// in IDS mode, we don't need TX queues
|
|
|
|
|
SCLogError("%s: cannot autocalculate mempool size without RX queues", iconf->iface);
|
|
|
|
|
err = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (iconf->nb_rx_desc == 0) {
|
|
|
|
|
SCLogError(
|
|
|
|
|
"%s: cannot autocalculate mempool size without RX descriptors", iconf->iface);
|
|
|
|
|
err = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (err) {
|
|
|
|
|
SCReturnInt(-EINVAL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
iconf->mempool_size = MempoolSizeCalculate(
|
|
|
|
|
iconf->nb_rx_queues, iconf->nb_rx_desc, iconf->nb_tx_queues, iconf->nb_tx_desc);
|
|
|
|
|
SCReturnInt(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (StringParseUint32(&iconf->mempool_size, 10, 0, entry_str) < 0) {
|
|
|
|
|
SCLogError("%s: mempool size entry contain non-numerical characters - \"%s\"", iconf->iface,
|
|
|
|
|
entry_str);
|
|
|
|
|
SCReturnInt(-EINVAL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (MempoolSizeCalculate(
|
|
|
|
|
iconf->nb_rx_queues, iconf->nb_rx_desc, iconf->nb_tx_queues, iconf->nb_tx_desc) >
|
|
|
|
|
iconf->mempool_size + 1) { // +1 to mask mempool size advice given in Suricata 7.0.x -
|
|
|
|
|
// mp_size should be n = (2^q - 1)
|
|
|
|
|
SCLogError("%s: mempool size is likely too small for the number of descriptors and queues, "
|
|
|
|
|
"set to \"auto\" or adjust to the value of \"%" PRIu32 "\"",
|
|
|
|
|
iconf->iface,
|
|
|
|
|
MempoolSizeCalculate(iconf->nb_rx_queues, iconf->nb_rx_desc, iconf->nb_tx_queues,
|
|
|
|
|
iconf->nb_tx_desc));
|
|
|
|
|
SCReturnInt(-ERANGE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (iconf->mempool_size == 0) {
|
|
|
|
|
SCLogError("%s: mempool size requires a positive integer", iconf->iface);
|
|
|
|
|
SCReturnInt(-ERANGE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SCReturnInt(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static uint32_t MempoolCacheSizeCalculate(uint32_t mp_sz)
|
|
|
|
|
{
|
|
|
|
|
// It is advised to have mempool cache size lower or equal to:
|
|
|
|
|
// RTE_MEMPOOL_CACHE_MAX_SIZE (by default 512) and "mempool-size / 1.5"
|
|
|
|
|
// and at the same time "mempool-size modulo cache_size == 0".
|
|
|
|
|
uint32_t max_cache_size = MIN(RTE_MEMPOOL_CACHE_MAX_SIZE, (uint32_t)(mp_sz / 1.5));
|
|
|
|
|
return GreatestDivisorUpTo(mp_sz, max_cache_size);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ConfigSetMempoolCacheSize(DPDKIfaceConfig *iconf, const char *entry_str)
|
|
|
|
|
{
|
|
|
|
|
SCEnter();
|
|
|
|
|
if (entry_str == NULL || entry_str[0] == '\0' || strcmp(entry_str, "auto") == 0) {
|
|
|
|
|
// calculate the mempool size based on the mempool size (it needs to be already filled in)
|
|
|
|
|
if (iconf->mempool_size == 0) {
|
|
|
|
|
SCLogError("%s: cannot calculate mempool cache size of a mempool with size %d",
|
|
|
|
|
iconf->iface, iconf->mempool_size);
|
|
|
|
|
SCReturnInt(-EINVAL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
iconf->mempool_cache_size_auto = true;
|
|
|
|
|
SCReturnInt(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
iconf->mempool_cache_size_auto = false;
|
|
|
|
|
if (StringParseUint32(&iconf->mempool_cache_size, 10, 0, entry_str) < 0) {
|
|
|
|
|
SCLogError("%s: mempool cache size entry contain non-numerical characters - \"%s\"",
|
|
|
|
|
iconf->iface, entry_str);
|
|
|
|
|
SCReturnInt(-EINVAL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (iconf->mempool_cache_size <= 0 || iconf->mempool_cache_size > RTE_MEMPOOL_CACHE_MAX_SIZE) {
|
|
|
|
|
SCLogError("%s: mempool cache size requires a positive number smaller than %" PRIu32,
|
|
|
|
|
iconf->iface, RTE_MEMPOOL_CACHE_MAX_SIZE);
|
|
|
|
|
SCReturnInt(-ERANGE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SCReturnInt(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ConfigSetRxDescriptors(DPDKIfaceConfig *iconf, const char *entry_str, uint16_t max_desc)
|
|
|
|
|
{
|
|
|
|
|
SCEnter();
|
|
|
|
|
if (entry_str == NULL || entry_str[0] == '\0') {
|
|
|
|
|
SCLogInfo("%s: number of RX descriptors not found, going with: %s", iconf->iface,
|
|
|
|
|
DPDK_CONFIG_DEFAULT_RX_DESCRIPTORS);
|
|
|
|
|
entry_str = DPDK_CONFIG_DEFAULT_RX_DESCRIPTORS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (strcmp(entry_str, "auto") == 0) {
|
|
|
|
|
iconf->nb_rx_desc = (uint16_t)GreatestPowOf2UpTo(max_desc);
|
|
|
|
|
SCReturnInt(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (StringParseUint16(&iconf->nb_rx_desc, 10, 0, entry_str) < 0) {
|
|
|
|
|
SCLogError("%s: RX descriptors entry contains non-numerical characters - \"%s\"",
|
|
|
|
|
iconf->iface, entry_str);
|
|
|
|
|
SCReturnInt(-EINVAL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (iconf->nb_rx_desc == 0) {
|
|
|
|
|
SCLogError("%s: positive number of RX descriptors is required", iconf->iface);
|
|
|
|
|
SCReturnInt(-ERANGE);
|
|
|
|
|
} else if (iconf->nb_rx_desc > max_desc) {
|
|
|
|
|
SCLogError("%s: number of RX descriptors cannot exceed %" PRIu16, iconf->iface, max_desc);
|
|
|
|
|
SCReturnInt(-ERANGE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SCReturnInt(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ConfigSetTxDescriptors(
|
|
|
|
|
DPDKIfaceConfig *iconf, const char *entry_str, uint16_t max_desc, bool iface_sends_pkts)
|
|
|
|
|
{
|
|
|
|
|
SCEnter();
|
|
|
|
|
if (entry_str == NULL || entry_str[0] == '\0') {
|
|
|
|
|
SCLogInfo("%s: number of TX descriptors not found, going with: %s", iconf->iface,
|
|
|
|
|
DPDK_CONFIG_DEFAULT_TX_DESCRIPTORS);
|
|
|
|
|
entry_str = DPDK_CONFIG_DEFAULT_TX_DESCRIPTORS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (strcmp(entry_str, "auto") == 0) {
|
|
|
|
|
if (iface_sends_pkts) {
|
|
|
|
|
iconf->nb_tx_desc = (uint16_t)GreatestPowOf2UpTo(max_desc);
|
|
|
|
|
} else {
|
|
|
|
|
iconf->nb_tx_desc = 0;
|
|
|
|
|
}
|
|
|
|
|
SCReturnInt(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (StringParseUint16(&iconf->nb_tx_desc, 10, 0, entry_str) < 0) {
|
|
|
|
|
SCLogError("%s: TX descriptors entry contains non-numerical characters - \"%s\"",
|
|
|
|
|
iconf->iface, entry_str);
|
|
|
|
|
SCReturnInt(-EINVAL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (iconf->nb_tx_desc == 0 && iface_sends_pkts) {
|
|
|
|
|
SCLogError("%s: positive number of TX descriptors is required", iconf->iface);
|
|
|
|
|
SCReturnInt(-ERANGE);
|
|
|
|
|
} else if (iconf->nb_tx_desc > max_desc) {
|
|
|
|
|
SCLogError("%s: number of TX descriptors cannot exceed %" PRIu16, iconf->iface, max_desc);
|
|
|
|
|
SCReturnInt(-ERANGE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SCReturnInt(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ConfigSetRSSHashFunctions(DPDKIfaceConfig *iconf, const char *entry_str)
|
|
|
|
|
{
|
|
|
|
|
SCEnter();
|
|
|
|
|
if (entry_str == NULL || entry_str[0] == '\0' || strcmp(entry_str, "auto") == 0) {
|
|
|
|
|
iconf->rss_hf = DPDK_CONFIG_DEFAULT_RSS_HASH_FUNCTIONS;
|
|
|
|
|
SCReturnInt(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (StringParseUint64(&iconf->rss_hf, 0, 0, entry_str) < 0) {
|
|
|
|
|
SCLogError("%s: RSS hash functions entry contain non-numerical characters - \"%s\"",
|
|
|
|
|
iconf->iface, entry_str);
|
|
|
|
|
SCReturnInt(-EINVAL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SCReturnInt(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ConfigSetMtu(DPDKIfaceConfig *iconf, intmax_t entry_int)
|
|
|
|
|
{
|
|
|
|
|
SCEnter();
|
|
|
|
|
if (entry_int < RTE_ETHER_MIN_MTU || entry_int > RTE_ETHER_MAX_JUMBO_FRAME_LEN) {
|
|
|
|
|
SCLogError("%s: MTU size can only be between %" PRIu32 " and %" PRIu32, iconf->iface,
|
|
|
|
|
RTE_ETHER_MIN_MTU, RTE_ETHER_MAX_JUMBO_FRAME_LEN);
|
|
|
|
|
SCReturnInt(-ERANGE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
iconf->mtu = (uint16_t)entry_int;
|
|
|
|
|
SCReturnInt(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ConfigSetLinkupTimeout(DPDKIfaceConfig *iconf, intmax_t entry_int)
|
|
|
|
|
{
|
|
|
|
|
SCEnter();
|
|
|
|
|
if (entry_int < 0 || entry_int > UINT16_MAX) {
|
|
|
|
|
SCLogError("%s: Link-up waiting timeout needs to be a positive number (up to %u) or 0 to "
|
|
|
|
|
"disable",
|
|
|
|
|
iconf->iface, UINT16_MAX);
|
|
|
|
|
SCReturnInt(-ERANGE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
iconf->linkup_timeout = (uint16_t)entry_int;
|
|
|
|
|
SCReturnInt(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool ConfigSetPromiscuousMode(DPDKIfaceConfig *iconf, int entry_bool)
|
|
|
|
|
{
|
|
|
|
|
SCEnter();
|
|
|
|
|
if (entry_bool)
|
|
|
|
|
iconf->flags |= DPDK_PROMISC;
|
|
|
|
|
|
|
|
|
|
SCReturnBool(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool ConfigSetMulticast(DPDKIfaceConfig *iconf, int entry_bool)
|
|
|
|
|
{
|
|
|
|
|
SCEnter();
|
|
|
|
|
if (entry_bool)
|
|
|
|
|
iconf->flags |= DPDK_MULTICAST; // enable
|
|
|
|
|
|
|
|
|
|
SCReturnBool(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ConfigSetChecksumChecks(DPDKIfaceConfig *iconf, int entry_bool)
|
|
|
|
|
{
|
|
|
|
|
SCEnter();
|
|
|
|
|
if (entry_bool)
|
|
|
|
|
iconf->checksum_mode = CHECKSUM_VALIDATION_ENABLE;
|
|
|
|
|
|
|
|
|
|
SCReturnInt(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ConfigSetChecksumOffload(DPDKIfaceConfig *iconf, int entry_bool)
|
|
|
|
|
{
|
|
|
|
|
SCEnter();
|
|
|
|
|
if (entry_bool)
|
|
|
|
|
iconf->flags |= DPDK_RX_CHECKSUM_OFFLOAD;
|
|
|
|
|
|
|
|
|
|
SCReturnInt(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ConfigSetVlanStrip(DPDKIfaceConfig *iconf, int entry_bool)
|
|
|
|
|
{
|
|
|
|
|
SCEnter();
|
|
|
|
|
iconf->vlan_strip_enabled = entry_bool;
|
|
|
|
|
SCReturn;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ConfigSetCopyIface(DPDKIfaceConfig *iconf, const char *entry_str)
|
|
|
|
|
{
|
|
|
|
|
SCEnter();
|
|
|
|
|
int retval;
|
|
|
|
|
|
|
|
|
|
if (entry_str == NULL || entry_str[0] == '\0' || strcmp(entry_str, "none") == 0) {
|
|
|
|
|
iconf->out_iface = NULL;
|
|
|
|
|
SCReturnInt(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
retval = rte_eth_dev_get_port_by_name(entry_str, &iconf->out_port_id);
|
|
|
|
|
if (retval < 0) {
|
|
|
|
|
SCLogError("%s: copy interface (%s) not found: %s", iconf->iface, entry_str,
|
|
|
|
|
rte_strerror(-retval));
|
|
|
|
|
SCReturnInt(retval);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
iconf->out_iface = entry_str;
|
|
|
|
|
SCReturnInt(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ConfigSetCopyMode(DPDKIfaceConfig *iconf, const char *entry_str)
|
|
|
|
|
{
|
|
|
|
|
SCEnter();
|
|
|
|
|
if (entry_str == NULL) {
|
|
|
|
|
SCLogWarning("%s: no copy mode specified, changing to %s ", iconf->iface,
|
|
|
|
|
DPDK_CONFIG_DEFAULT_COPY_MODE);
|
|
|
|
|
entry_str = DPDK_CONFIG_DEFAULT_COPY_MODE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (strcmp(entry_str, "none") != 0 && strcmp(entry_str, "tap") != 0 &&
|
|
|
|
|
strcmp(entry_str, "ips") != 0) {
|
|
|
|
|
SCLogWarning("%s: copy mode \"%s\" is not one of the possible values (none|tap|ips). "
|
|
|
|
|
"Changing to %s",
|
|
|
|
|
entry_str, iconf->iface, DPDK_CONFIG_DEFAULT_COPY_MODE);
|
|
|
|
|
entry_str = DPDK_CONFIG_DEFAULT_COPY_MODE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (strcmp(entry_str, "none") == 0) {
|
|
|
|
|
iconf->copy_mode = DPDK_COPY_MODE_NONE;
|
|
|
|
|
} else if (strcmp(entry_str, "tap") == 0) {
|
|
|
|
|
iconf->copy_mode = DPDK_COPY_MODE_TAP;
|
|
|
|
|
} else if (strcmp(entry_str, "ips") == 0) {
|
|
|
|
|
iconf->copy_mode = DPDK_COPY_MODE_IPS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SCReturnInt(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ConfigSetCopyIfaceSettings(DPDKIfaceConfig *iconf, const char *iface, const char *mode)
|
|
|
|
|
{
|
|
|
|
|
SCEnter();
|
|
|
|
|
int retval;
|
|
|
|
|
|
|
|
|
|
retval = ConfigSetCopyIface(iconf, iface);
|
|
|
|
|
if (retval < 0)
|
|
|
|
|
SCReturnInt(retval);
|
|
|
|
|
|
|
|
|
|
retval = ConfigSetCopyMode(iconf, mode);
|
|
|
|
|
if (retval < 0)
|
|
|
|
|
SCReturnInt(retval);
|
|
|
|
|
|
|
|
|
|
if (iconf->copy_mode == DPDK_COPY_MODE_NONE) {
|
|
|
|
|
if (iconf->out_iface != NULL)
|
|
|
|
|
iconf->out_iface = NULL;
|
|
|
|
|
SCReturnInt(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (iconf->out_iface == NULL || strlen(iconf->out_iface) <= 0) {
|
|
|
|
|
SCLogError("%s: copy mode enabled but interface not set", iconf->iface);
|
|
|
|
|
SCReturnInt(-EINVAL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SCReturnInt(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ConfigLoad(DPDKIfaceConfig *iconf, const char *iface)
|
|
|
|
|
{
|
|
|
|
|
SCEnter();
|
|
|
|
|
int retval;
|
|
|
|
|
SCConfNode *if_root;
|
|
|
|
|
SCConfNode *if_default;
|
|
|
|
|
const char *entry_str = NULL;
|
|
|
|
|
intmax_t entry_int = 0;
|
|
|
|
|
int entry_bool = 0;
|
|
|
|
|
const char *copy_iface_str = NULL;
|
|
|
|
|
const char *copy_mode_str = NULL;
|
|
|
|
|
|
|
|
|
|
ConfigSetIface(iconf, iface);
|
|
|
|
|
struct rte_eth_dev_info dev_info = { 0 };
|
|
|
|
|
retval = rte_eth_dev_info_get(iconf->port_id, &dev_info);
|
|
|
|
|
if (retval < 0) {
|
|
|
|
|
SCLogError("%s: getting device info failed: %s", iconf->iface, rte_strerror(-retval));
|
|
|
|
|
SCReturnInt(retval);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
retval = SCConfSetRootAndDefaultNodes("dpdk.interfaces", iconf->iface, &if_root, &if_default);
|
|
|
|
|
if (retval < 0) {
|
|
|
|
|
FatalError("failed to find DPDK configuration for the interface %s", iconf->iface);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
retval = SCConfGetChildValueWithDefault(if_root, if_default, dpdk_yaml.threads, &entry_str) != 1
|
|
|
|
|
? ConfigSetThreads(iconf, DPDK_CONFIG_DEFAULT_THREADS)
|
|
|
|
|
: ConfigSetThreads(iconf, entry_str);
|
|
|
|
|
if (retval < 0)
|
|
|
|
|
SCReturnInt(retval);
|
|
|
|
|
|
|
|
|
|
bool irq_enable;
|
|
|
|
|
retval = SCConfGetChildValueBoolWithDefault(
|
|
|
|
|
if_root, if_default, dpdk_yaml.irq_mode, &entry_bool);
|
|
|
|
|
if (retval != 1) {
|
|
|
|
|
irq_enable = DPDK_CONFIG_DEFAULT_INTERRUPT_MODE;
|
|
|
|
|
} else {
|
|
|
|
|
irq_enable = entry_bool ? true : false;
|
|
|
|
|
}
|
|
|
|
|
retval = ConfigSetInterruptMode(iconf, irq_enable);
|
|
|
|
|
if (retval != true)
|
|
|
|
|
SCReturnInt(-EINVAL);
|
|
|
|
|
|
|
|
|
|
retval = SCConfGetChildValueWithDefault(
|
|
|
|
|
if_root, if_default, dpdk_yaml.copy_mode, ©_mode_str);
|
|
|
|
|
if (retval != 1) {
|
|
|
|
|
copy_mode_str = DPDK_CONFIG_DEFAULT_COPY_MODE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
retval = SCConfGetChildValueWithDefault(
|
|
|
|
|
if_root, if_default, dpdk_yaml.rx_descriptors, &entry_str) != 1
|
|
|
|
|
? ConfigSetRxDescriptors(iconf, DPDK_CONFIG_DEFAULT_RX_DESCRIPTORS,
|
|
|
|
|
dev_info.rx_desc_lim.nb_max)
|
|
|
|
|
: ConfigSetRxDescriptors(iconf, entry_str, dev_info.rx_desc_lim.nb_max);
|
|
|
|
|
if (retval < 0)
|
|
|
|
|
SCReturnInt(retval);
|
|
|
|
|
|
|
|
|
|
bool iface_sends_pkts = ConfigIfaceSendsPkts(copy_mode_str);
|
|
|
|
|
retval = SCConfGetChildValueWithDefault(
|
|
|
|
|
if_root, if_default, dpdk_yaml.tx_descriptors, &entry_str) != 1
|
|
|
|
|
? ConfigSetTxDescriptors(iconf, DPDK_CONFIG_DEFAULT_TX_DESCRIPTORS,
|
|
|
|
|
dev_info.tx_desc_lim.nb_max, iface_sends_pkts)
|
|
|
|
|
: ConfigSetTxDescriptors(
|
|
|
|
|
iconf, entry_str, dev_info.tx_desc_lim.nb_max, iface_sends_pkts);
|
|
|
|
|
if (retval < 0)
|
|
|
|
|
SCReturnInt(retval);
|
|
|
|
|
|
|
|
|
|
// currently only mapping "1 thread == 1 RX (and 1 TX queue in IPS mode)" is supported
|
|
|
|
|
retval = ConfigSetRxQueues(iconf, iconf->threads, dev_info.max_rx_queues);
|
|
|
|
|
if (retval < 0) {
|
|
|
|
|
SCLogError("%s: too many threads configured - reduce thread count to: %" PRIu16,
|
|
|
|
|
iconf->iface, dev_info.max_rx_queues);
|
|
|
|
|
SCReturnInt(retval);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// currently only mapping "1 thread == 1 RX (and 1 TX queue in IPS mode)" is supported
|
|
|
|
|
uint16_t tx_queues = iconf->nb_tx_desc > 0 ? iconf->threads : 0;
|
|
|
|
|
retval = ConfigSetTxQueues(iconf, tx_queues, dev_info.max_tx_queues, iface_sends_pkts);
|
|
|
|
|
if (retval < 0) {
|
|
|
|
|
SCLogError("%s: too many threads configured - reduce thread count to: %" PRIu16,
|
|
|
|
|
iconf->iface, dev_info.max_tx_queues);
|
|
|
|
|
SCReturnInt(retval);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
retval = SCConfGetChildValueWithDefault(
|
|
|
|
|
if_root, if_default, dpdk_yaml.mempool_size, &entry_str) != 1
|
|
|
|
|
? ConfigSetMempoolSize(iconf, DPDK_CONFIG_DEFAULT_MEMPOOL_SIZE)
|
|
|
|
|
: ConfigSetMempoolSize(iconf, entry_str);
|
|
|
|
|
if (retval < 0)
|
|
|
|
|
SCReturnInt(retval);
|
|
|
|
|
|
|
|
|
|
retval = SCConfGetChildValueWithDefault(
|
|
|
|
|
if_root, if_default, dpdk_yaml.mempool_cache_size, &entry_str) != 1
|
|
|
|
|
? ConfigSetMempoolCacheSize(iconf, DPDK_CONFIG_DEFAULT_MEMPOOL_CACHE_SIZE)
|
|
|
|
|
: ConfigSetMempoolCacheSize(iconf, entry_str);
|
|
|
|
|
if (retval < 0)
|
|
|
|
|
SCReturnInt(retval);
|
|
|
|
|
|
|
|
|
|
retval = SCConfGetChildValueIntWithDefault(if_root, if_default, dpdk_yaml.mtu, &entry_int) != 1
|
|
|
|
|
? ConfigSetMtu(iconf, DPDK_CONFIG_DEFAULT_MTU)
|
|
|
|
|
: ConfigSetMtu(iconf, entry_int);
|
|
|
|
|
if (retval < 0)
|
|
|
|
|
SCReturnInt(retval);
|
|
|
|
|
|
|
|
|
|
retval = SCConfGetChildValueWithDefault(if_root, if_default, dpdk_yaml.rss_hf, &entry_str) != 1
|
|
|
|
|
? ConfigSetRSSHashFunctions(iconf, NULL)
|
|
|
|
|
: ConfigSetRSSHashFunctions(iconf, entry_str);
|
|
|
|
|
if (retval < 0)
|
|
|
|
|
SCReturnInt(retval);
|
|
|
|
|
|
|
|
|
|
retval = SCConfGetChildValueBoolWithDefault(
|
|
|
|
|
if_root, if_default, dpdk_yaml.promisc, &entry_bool) != 1
|
|
|
|
|
? ConfigSetPromiscuousMode(iconf, DPDK_CONFIG_DEFAULT_PROMISCUOUS_MODE)
|
|
|
|
|
: ConfigSetPromiscuousMode(iconf, entry_bool);
|
|
|
|
|
if (retval != true)
|
|
|
|
|
SCReturnInt(-EINVAL);
|
|
|
|
|
|
|
|
|
|
retval = SCConfGetChildValueBoolWithDefault(
|
|
|
|
|
if_root, if_default, dpdk_yaml.multicast, &entry_bool) != 1
|
|
|
|
|
? ConfigSetMulticast(iconf, DPDK_CONFIG_DEFAULT_MULTICAST_MODE)
|
|
|
|
|
: ConfigSetMulticast(iconf, entry_bool);
|
|
|
|
|
if (retval != true)
|
|
|
|
|
SCReturnInt(-EINVAL);
|
|
|
|
|
|
|
|
|
|
retval = SCConfGetChildValueBoolWithDefault(
|
|
|
|
|
if_root, if_default, dpdk_yaml.checksum_checks, &entry_bool) != 1
|
|
|
|
|
? ConfigSetChecksumChecks(iconf, DPDK_CONFIG_DEFAULT_CHECKSUM_VALIDATION)
|
|
|
|
|
: ConfigSetChecksumChecks(iconf, entry_bool);
|
|
|
|
|
if (retval < 0)
|
|
|
|
|
SCReturnInt(retval);
|
|
|
|
|
|
|
|
|
|
retval = SCConfGetChildValueBoolWithDefault(
|
|
|
|
|
if_root, if_default, dpdk_yaml.checksum_checks_offload, &entry_bool) != 1
|
|
|
|
|
? ConfigSetChecksumOffload(
|
|
|
|
|
iconf, DPDK_CONFIG_DEFAULT_CHECKSUM_VALIDATION_OFFLOAD)
|
|
|
|
|
: ConfigSetChecksumOffload(iconf, entry_bool);
|
|
|
|
|
if (retval < 0)
|
|
|
|
|
SCReturnInt(retval);
|
|
|
|
|
|
|
|
|
|
retval = SCConfGetChildValueBoolWithDefault(
|
|
|
|
|
if_root, if_default, dpdk_yaml.vlan_strip_offload, &entry_bool);
|
|
|
|
|
if (retval != 1) {
|
|
|
|
|
ConfigSetVlanStrip(iconf, DPDK_CONFIG_DEFAULT_VLAN_STRIP);
|
|
|
|
|
} else {
|
|
|
|
|
ConfigSetVlanStrip(iconf, entry_bool);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
retval = SCConfGetChildValueIntWithDefault(
|
|
|
|
|
if_root, if_default, dpdk_yaml.linkup_timeout, &entry_int) != 1
|
|
|
|
|
? ConfigSetLinkupTimeout(iconf, DPDK_CONFIG_DEFAULT_LINKUP_TIMEOUT)
|
|
|
|
|
: ConfigSetLinkupTimeout(iconf, entry_int);
|
|
|
|
|
if (retval < 0)
|
|
|
|
|
SCReturnInt(retval);
|
|
|
|
|
|
|
|
|
|
retval = SCConfGetChildValueWithDefault(
|
|
|
|
|
if_root, if_default, dpdk_yaml.copy_iface, ©_iface_str);
|
|
|
|
|
if (retval != 1) {
|
|
|
|
|
copy_iface_str = DPDK_CONFIG_DEFAULT_COPY_INTERFACE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
retval = ConfigSetCopyIfaceSettings(iconf, copy_iface_str, copy_mode_str);
|
|
|
|
|
if (retval < 0)
|
|
|
|
|
SCReturnInt(retval);
|
|
|
|
|
|
|
|
|
|
SCReturnInt(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool ConfigThreadsGenericIsValid(uint16_t iface_threads, ThreadsAffinityType *wtaf)
|
|
|
|
|
{
|
|
|
|
|
static uint32_t total_cpus = 0;
|
|
|
|
|
total_cpus += iface_threads;
|
|
|
|
|
if (wtaf == NULL) {
|
|
|
|
|
SCLogError("Specify worker-cpu-set list in the threading section");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (total_cpus > UtilAffinityGetAffinedCPUNum(wtaf)) {
|
|
|
|
|
SCLogError("Interfaces requested more cores than configured in the worker-cpu-set "
|
|
|
|
|
"threading section (requested %d configured %d",
|
|
|
|
|
total_cpus, UtilAffinityGetAffinedCPUNum(wtaf));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool ConfigThreadsInterfaceIsValid(uint16_t iface_threads, ThreadsAffinityType *itaf)
|
|
|
|
|
{
|
|
|
|
|
if (iface_threads > UtilAffinityGetAffinedCPUNum(itaf)) {
|
|
|
|
|
SCLogError("Interface requested more cores than configured in the interface-specific "
|
|
|
|
|
"threading section (requested %d configured %d",
|
|
|
|
|
iface_threads, UtilAffinityGetAffinedCPUNum(itaf));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool ConfigIsThreadingValid(uint16_t iface_threads, const char *iface)
|
|
|
|
|
{
|
|
|
|
|
ThreadsAffinityType *itaf = GetAffinityTypeForNameAndIface("worker-cpu-set", iface);
|
|
|
|
|
ThreadsAffinityType *wtaf = GetAffinityTypeForNameAndIface("worker-cpu-set", NULL);
|
|
|
|
|
if (itaf && !ConfigThreadsInterfaceIsValid(iface_threads, itaf)) {
|
|
|
|
|
return false;
|
|
|
|
|
} else if (itaf == NULL && !ConfigThreadsGenericIsValid(iface_threads, wtaf)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static DPDKIfaceConfig *ConfigParse(const char *iface)
|
|
|
|
|
{
|
|
|
|
|
SCEnter();
|
|
|
|
|
int retval;
|
|
|
|
|
DPDKIfaceConfig *iconf = NULL;
|
|
|
|
|
if (iface == NULL)
|
|
|
|
|
FatalError("DPDK interface is NULL");
|
|
|
|
|
|
|
|
|
|
ConfigInit(&iconf);
|
|
|
|
|
retval = ConfigLoad(iconf, iface);
|
|
|
|
|
if (retval < 0 || !ConfigIsThreadingValid(iconf->threads, iface)) {
|
|
|
|
|
iconf->DerefFunc(iconf);
|
|
|
|
|
SCReturnPtr(NULL, "void *");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SCReturnPtr(iconf, "DPDKIfaceConfig *");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void DeviceSetPMDSpecificRSS(struct rte_eth_rss_conf *rss_conf, const char *driver_name)
|
|
|
|
|
{
|
|
|
|
|
if (strcmp(driver_name, "net_i40e") == 0)
|
|
|
|
|
i40eDeviceSetRSSConf(rss_conf);
|
|
|
|
|
if (strcmp(driver_name, "net_ice") == 0)
|
|
|
|
|
iceDeviceSetRSSConf(rss_conf);
|
|
|
|
|
if (strcmp(driver_name, "net_ixgbe") == 0)
|
|
|
|
|
ixgbeDeviceSetRSSHashFunction(&rss_conf->rss_hf);
|
|
|
|
|
if (strcmp(driver_name, "net_e1000_igb") == 0)
|
|
|
|
|
rss_conf->rss_hf = (RTE_ETH_RSS_IPV4 | RTE_ETH_RSS_IPV6 | RTE_ETH_RSS_IPV6_EX);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Returns -1 if no bit is set
|
|
|
|
|
static int32_t GetFirstSetBitPosition(uint64_t bits)
|
|
|
|
|
{
|
|
|
|
|
for (int32_t i = 0; i < 64; i++) {
|
|
|
|
|
if (bits & BIT_U64(i))
|
|
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void DumpRSSFlags(const uint64_t requested, const uint64_t actual)
|
|
|
|
|
{
|
|
|
|
|
SCLogConfig("REQUESTED (groups):");
|
|
|
|
|
|
|
|
|
|
SCLogConfig(
|
|
|
|
|
"RTE_ETH_RSS_IP %sset", ((requested & RTE_ETH_RSS_IP) == RTE_ETH_RSS_IP) ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_TCP %sset",
|
|
|
|
|
((requested & RTE_ETH_RSS_TCP) == RTE_ETH_RSS_TCP) ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_UDP %sset",
|
|
|
|
|
((requested & RTE_ETH_RSS_UDP) == RTE_ETH_RSS_UDP) ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_SCTP %sset",
|
|
|
|
|
((requested & RTE_ETH_RSS_SCTP) == RTE_ETH_RSS_SCTP) ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_TUNNEL %sset",
|
|
|
|
|
((requested & RTE_ETH_RSS_TUNNEL) == RTE_ETH_RSS_TUNNEL) ? "" : "NOT ");
|
|
|
|
|
|
|
|
|
|
SCLogConfig("REQUESTED (individual):");
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_IPV4 (Bit position: %d) %sset",
|
|
|
|
|
GetFirstSetBitPosition(RTE_ETH_RSS_IPV4), (requested & RTE_ETH_RSS_IPV4) ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_FRAG_IPV4 (Bit position: %d) %sset",
|
|
|
|
|
GetFirstSetBitPosition(RTE_ETH_RSS_FRAG_IPV4),
|
|
|
|
|
(requested & RTE_ETH_RSS_FRAG_IPV4) ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_NONFRAG_IPV4_TCP (Bit position: %d) %sset",
|
|
|
|
|
GetFirstSetBitPosition(RTE_ETH_RSS_NONFRAG_IPV4_TCP),
|
|
|
|
|
(requested & RTE_ETH_RSS_NONFRAG_IPV4_TCP) ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_NONFRAG_IPV4_UDP (Bit position: %d) %sset",
|
|
|
|
|
GetFirstSetBitPosition(RTE_ETH_RSS_NONFRAG_IPV4_UDP),
|
|
|
|
|
(requested & RTE_ETH_RSS_NONFRAG_IPV4_UDP) ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_NONFRAG_IPV4_SCTP (Bit position: %d) %sset",
|
|
|
|
|
GetFirstSetBitPosition(RTE_ETH_RSS_NONFRAG_IPV4_SCTP),
|
|
|
|
|
(requested & RTE_ETH_RSS_NONFRAG_IPV4_SCTP) ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_NONFRAG_IPV4_OTHER (Bit position: %d) %sset",
|
|
|
|
|
GetFirstSetBitPosition(RTE_ETH_RSS_NONFRAG_IPV4_OTHER),
|
|
|
|
|
(requested & RTE_ETH_RSS_NONFRAG_IPV4_OTHER) ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_IPV6 (Bit position: %d) %sset",
|
|
|
|
|
GetFirstSetBitPosition(RTE_ETH_RSS_IPV6), (requested & RTE_ETH_RSS_IPV6) ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_FRAG_IPV6 (Bit position: %d) %sset",
|
|
|
|
|
GetFirstSetBitPosition(RTE_ETH_RSS_FRAG_IPV6),
|
|
|
|
|
(requested & RTE_ETH_RSS_FRAG_IPV6) ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_NONFRAG_IPV6_TCP (Bit position: %d) %sset",
|
|
|
|
|
GetFirstSetBitPosition(RTE_ETH_RSS_NONFRAG_IPV6_TCP),
|
|
|
|
|
(requested & RTE_ETH_RSS_NONFRAG_IPV6_TCP) ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_NONFRAG_IPV6_UDP (Bit position: %d) %sset",
|
|
|
|
|
GetFirstSetBitPosition(RTE_ETH_RSS_NONFRAG_IPV6_UDP),
|
|
|
|
|
(requested & RTE_ETH_RSS_NONFRAG_IPV6_UDP) ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_NONFRAG_IPV6_SCTP (Bit position: %d) %sset",
|
|
|
|
|
GetFirstSetBitPosition(RTE_ETH_RSS_NONFRAG_IPV6_SCTP),
|
|
|
|
|
(requested & RTE_ETH_RSS_NONFRAG_IPV6_SCTP) ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_NONFRAG_IPV6_OTHER (Bit position: %d) %sset",
|
|
|
|
|
GetFirstSetBitPosition(RTE_ETH_RSS_NONFRAG_IPV6_OTHER),
|
|
|
|
|
(requested & RTE_ETH_RSS_NONFRAG_IPV6_OTHER) ? "" : "NOT ");
|
|
|
|
|
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_L2_PAYLOAD (Bit position: %d) %sset",
|
|
|
|
|
GetFirstSetBitPosition(RTE_ETH_RSS_L2_PAYLOAD),
|
|
|
|
|
(requested & RTE_ETH_RSS_L2_PAYLOAD) ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_IPV6_EX (Bit position: %d) %sset",
|
|
|
|
|
GetFirstSetBitPosition(RTE_ETH_RSS_IPV6_EX),
|
|
|
|
|
(requested & RTE_ETH_RSS_IPV6_EX) ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_IPV6_TCP_EX (Bit position: %d) %sset",
|
|
|
|
|
GetFirstSetBitPosition(RTE_ETH_RSS_IPV6_TCP_EX),
|
|
|
|
|
(requested & RTE_ETH_RSS_IPV6_TCP_EX) ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_IPV6_UDP_EX (Bit position: %d) %sset",
|
|
|
|
|
GetFirstSetBitPosition(RTE_ETH_RSS_IPV6_UDP_EX),
|
|
|
|
|
(requested & RTE_ETH_RSS_IPV6_UDP_EX) ? "" : "NOT ");
|
|
|
|
|
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_PORT (Bit position: %d) %sset",
|
|
|
|
|
GetFirstSetBitPosition(RTE_ETH_RSS_PORT), (requested & RTE_ETH_RSS_PORT) ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_VXLAN (Bit position: %d) %sset",
|
|
|
|
|
GetFirstSetBitPosition(RTE_ETH_RSS_VXLAN),
|
|
|
|
|
(requested & RTE_ETH_RSS_VXLAN) ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_NVGRE (Bit position: %d) %sset",
|
|
|
|
|
GetFirstSetBitPosition(RTE_ETH_RSS_NVGRE),
|
|
|
|
|
(requested & RTE_ETH_RSS_NVGRE) ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_GTPU (Bit position: %d) %sset",
|
|
|
|
|
GetFirstSetBitPosition(RTE_ETH_RSS_GTPU), (requested & RTE_ETH_RSS_GTPU) ? "" : "NOT ");
|
|
|
|
|
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_L3_SRC_ONLY (Bit position: %d) %sset",
|
|
|
|
|
GetFirstSetBitPosition(RTE_ETH_RSS_L3_SRC_ONLY),
|
|
|
|
|
(requested & RTE_ETH_RSS_L3_SRC_ONLY) ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_L3_DST_ONLY (Bit position: %d) %sset",
|
|
|
|
|
GetFirstSetBitPosition(RTE_ETH_RSS_L3_DST_ONLY),
|
|
|
|
|
(requested & RTE_ETH_RSS_L3_DST_ONLY) ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_L4_SRC_ONLY (Bit position: %d) %sset",
|
|
|
|
|
GetFirstSetBitPosition(RTE_ETH_RSS_L4_SRC_ONLY),
|
|
|
|
|
(requested & RTE_ETH_RSS_L4_SRC_ONLY) ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_L4_DST_ONLY (Bit position: %d) %sset",
|
|
|
|
|
GetFirstSetBitPosition(RTE_ETH_RSS_L4_DST_ONLY),
|
|
|
|
|
(requested & RTE_ETH_RSS_L4_DST_ONLY) ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("ACTUAL (group):");
|
|
|
|
|
SCLogConfig(
|
|
|
|
|
"RTE_ETH_RSS_IP %sset", ((actual & RTE_ETH_RSS_IP) == RTE_ETH_RSS_IP) ? "" : "NOT ");
|
|
|
|
|
SCLogConfig(
|
|
|
|
|
"RTE_ETH_RSS_TCP %sset", ((actual & RTE_ETH_RSS_TCP) == RTE_ETH_RSS_TCP) ? "" : "NOT ");
|
|
|
|
|
SCLogConfig(
|
|
|
|
|
"RTE_ETH_RSS_UDP %sset", ((actual & RTE_ETH_RSS_UDP) == RTE_ETH_RSS_UDP) ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_SCTP %sset",
|
|
|
|
|
((actual & RTE_ETH_RSS_SCTP) == RTE_ETH_RSS_SCTP) ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_TUNNEL %sset",
|
|
|
|
|
((actual & RTE_ETH_RSS_TUNNEL) == RTE_ETH_RSS_TUNNEL) ? "" : "NOT ");
|
|
|
|
|
|
|
|
|
|
SCLogConfig("ACTUAL (individual flags):");
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_IPV4 %sset", (actual & RTE_ETH_RSS_IPV4) ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_FRAG_IPV4 %sset", (actual & RTE_ETH_RSS_FRAG_IPV4) ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_NONFRAG_IPV4_TCP %sset",
|
|
|
|
|
(actual & RTE_ETH_RSS_NONFRAG_IPV4_TCP) ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_NONFRAG_IPV4_UDP %sset",
|
|
|
|
|
(actual & RTE_ETH_RSS_NONFRAG_IPV4_UDP) ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_NONFRAG_IPV4_SCTP %sset",
|
|
|
|
|
(actual & RTE_ETH_RSS_NONFRAG_IPV4_SCTP) ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_NONFRAG_IPV4_OTHER %sset",
|
|
|
|
|
(actual & RTE_ETH_RSS_NONFRAG_IPV4_OTHER) ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_IPV6 %sset", (actual & RTE_ETH_RSS_IPV6) ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_FRAG_IPV6 %sset", (actual & RTE_ETH_RSS_FRAG_IPV6) ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_NONFRAG_IPV6_TCP %sset",
|
|
|
|
|
(actual & RTE_ETH_RSS_NONFRAG_IPV6_TCP) ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_NONFRAG_IPV6_UDP %sset",
|
|
|
|
|
(actual & RTE_ETH_RSS_NONFRAG_IPV6_UDP) ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_NONFRAG_IPV6_SCTP %sset",
|
|
|
|
|
(actual & RTE_ETH_RSS_NONFRAG_IPV6_SCTP) ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_NONFRAG_IPV6_OTHER %sset",
|
|
|
|
|
(actual & RTE_ETH_RSS_NONFRAG_IPV6_OTHER) ? "" : "NOT ");
|
|
|
|
|
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_L2_PAYLOAD %sset", (actual & RTE_ETH_RSS_L2_PAYLOAD) ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_IPV6_EX %sset", (actual & RTE_ETH_RSS_IPV6_EX) ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_IPV6_TCP_EX %sset", (actual & RTE_ETH_RSS_IPV6_TCP_EX) ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_IPV6_UDP_EX %sset", (actual & RTE_ETH_RSS_IPV6_UDP_EX) ? "" : "NOT ");
|
|
|
|
|
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_PORT %sset", (actual & RTE_ETH_RSS_PORT) ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_VXLAN %sset", (actual & RTE_ETH_RSS_VXLAN) ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_NVGRE %sset", (actual & RTE_ETH_RSS_NVGRE) ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_GTPU %sset", (actual & RTE_ETH_RSS_GTPU) ? "" : "NOT ");
|
|
|
|
|
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_L3_SRC_ONLY %sset", (actual & RTE_ETH_RSS_L3_SRC_ONLY) ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_L3_DST_ONLY %sset", (actual & RTE_ETH_RSS_L3_DST_ONLY) ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_L4_SRC_ONLY %sset", (actual & RTE_ETH_RSS_L4_SRC_ONLY) ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RSS_L4_DST_ONLY %sset", (actual & RTE_ETH_RSS_L4_DST_ONLY) ? "" : "NOT ");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void DumpRXOffloadCapabilities(const uint64_t rx_offld_capa)
|
|
|
|
|
{
|
|
|
|
|
SCLogConfig("RTE_ETH_RX_OFFLOAD_VLAN_STRIP - %savailable",
|
|
|
|
|
rx_offld_capa & RTE_ETH_RX_OFFLOAD_VLAN_STRIP ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RX_OFFLOAD_IPV4_CKSUM - %savailable",
|
|
|
|
|
rx_offld_capa & RTE_ETH_RX_OFFLOAD_IPV4_CKSUM ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RX_OFFLOAD_UDP_CKSUM - %savailable",
|
|
|
|
|
rx_offld_capa & RTE_ETH_RX_OFFLOAD_UDP_CKSUM ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RX_OFFLOAD_TCP_CKSUM - %savailable",
|
|
|
|
|
rx_offld_capa & RTE_ETH_RX_OFFLOAD_TCP_CKSUM ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RX_OFFLOAD_TCP_LRO - %savailable",
|
|
|
|
|
rx_offld_capa & RTE_ETH_RX_OFFLOAD_TCP_LRO ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RX_OFFLOAD_QINQ_STRIP - %savailable",
|
|
|
|
|
rx_offld_capa & RTE_ETH_RX_OFFLOAD_QINQ_STRIP ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RX_OFFLOAD_OUTER_IPV4_CKSUM - %savailable",
|
|
|
|
|
rx_offld_capa & RTE_ETH_RX_OFFLOAD_OUTER_IPV4_CKSUM ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RX_OFFLOAD_MACSEC_STRIP - %savailable",
|
|
|
|
|
rx_offld_capa & RTE_ETH_RX_OFFLOAD_MACSEC_STRIP ? "" : "NOT ");
|
|
|
|
|
#if RTE_VERSION < RTE_VERSION_NUM(22, 11, 0, 0)
|
|
|
|
|
SCLogConfig("RTE_ETH_RX_OFFLOAD_HEADER_SPLIT - %savailable",
|
|
|
|
|
rx_offld_capa & RTE_ETH_RX_OFFLOAD_HEADER_SPLIT ? "" : "NOT ");
|
|
|
|
|
#endif
|
|
|
|
|
SCLogConfig("RTE_ETH_RX_OFFLOAD_VLAN_FILTER - %savailable",
|
|
|
|
|
rx_offld_capa & RTE_ETH_RX_OFFLOAD_VLAN_FILTER ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RX_OFFLOAD_VLAN_EXTEND - %savailable",
|
|
|
|
|
rx_offld_capa & RTE_ETH_RX_OFFLOAD_VLAN_EXTEND ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RX_OFFLOAD_SCATTER - %savailable",
|
|
|
|
|
rx_offld_capa & RTE_ETH_RX_OFFLOAD_SCATTER ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RX_OFFLOAD_TIMESTAMP - %savailable",
|
|
|
|
|
rx_offld_capa & RTE_ETH_RX_OFFLOAD_TIMESTAMP ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RX_OFFLOAD_SECURITY - %savailable",
|
|
|
|
|
rx_offld_capa & RTE_ETH_RX_OFFLOAD_SECURITY ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RX_OFFLOAD_KEEP_CRC - %savailable",
|
|
|
|
|
rx_offld_capa & RTE_ETH_RX_OFFLOAD_KEEP_CRC ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RX_OFFLOAD_SCTP_CKSUM - %savailable",
|
|
|
|
|
rx_offld_capa & RTE_ETH_RX_OFFLOAD_SCTP_CKSUM ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RX_OFFLOAD_OUTER_UDP_CKSUM - %savailable",
|
|
|
|
|
rx_offld_capa & RTE_ETH_RX_OFFLOAD_OUTER_UDP_CKSUM ? "" : "NOT ");
|
|
|
|
|
SCLogConfig("RTE_ETH_RX_OFFLOAD_RSS_HASH - %savailable",
|
|
|
|
|
rx_offld_capa & RTE_ETH_RX_OFFLOAD_RSS_HASH ? "" : "NOT ");
|
|
|
|
|
#if RTE_VERSION >= RTE_VERSION_NUM(20, 11, 0, 0)
|
|
|
|
|
SCLogConfig("RTE_ETH_RX_OFFLOAD_BUFFER_SPLIT - %savailable",
|
|
|
|
|
rx_offld_capa & RTE_ETH_RX_OFFLOAD_BUFFER_SPLIT ? "" : "NOT ");
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int DeviceValidateMTU(const DPDKIfaceConfig *iconf, const struct rte_eth_dev_info *dev_info)
|
|
|
|
|
{
|
|
|
|
|
SCEnter();
|
|
|
|
|
if (iconf->mtu > dev_info->max_mtu || iconf->mtu < dev_info->min_mtu) {
|
|
|
|
|
SCLogError("%s: MTU out of bounds. "
|
|
|
|
|
"Min MTU: %" PRIu16 " Max MTU: %" PRIu16,
|
|
|
|
|
iconf->iface, dev_info->min_mtu, dev_info->max_mtu);
|
|
|
|
|
SCReturnInt(-ERANGE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if RTE_VERSION < RTE_VERSION_NUM(21, 11, 0, 0)
|
|
|
|
|
// check if jumbo frames are set and are available
|
|
|
|
|
if (iconf->mtu > RTE_ETHER_MAX_LEN &&
|
|
|
|
|
!(dev_info->rx_offload_capa & DEV_RX_OFFLOAD_JUMBO_FRAME)) {
|
|
|
|
|
SCLogError("%s: jumbo frames not supported, set MTU to 1500", iconf->iface);
|
|
|
|
|
SCReturnInt(-EINVAL);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
SCReturnInt(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void DeviceSetMTU(struct rte_eth_conf *port_conf, uint16_t mtu)
|
|
|
|
|
{
|
|
|
|
|
#if RTE_VERSION >= RTE_VERSION_NUM(21, 11, 0, 0)
|
|
|
|
|
port_conf->rxmode.mtu = mtu;
|
|
|
|
|
#else
|
|
|
|
|
port_conf->rxmode.max_rx_pkt_len = mtu;
|
|
|
|
|
if (mtu > RTE_ETHER_MAX_LEN) {
|
|
|
|
|
port_conf->rxmode.offloads |= DEV_RX_OFFLOAD_JUMBO_FRAME;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void PortConfSetInterruptMode(const DPDKIfaceConfig *iconf, struct rte_eth_conf *port_conf)
|
|
|
|
|
{
|
|
|
|
|
SCLogConfig("%s: interrupt mode is %s", iconf->iface,
|
|
|
|
|
iconf->flags & DPDK_IRQ_MODE ? "enabled" : "disabled");
|
|
|
|
|
if (iconf->flags & DPDK_IRQ_MODE)
|
|
|
|
|
port_conf->intr_conf.rxq = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void PortConfSetRSSConf(const DPDKIfaceConfig *iconf,
|
|
|
|
|
const struct rte_eth_dev_info *dev_info, struct rte_eth_conf *port_conf)
|
|
|
|
|
{
|
|
|
|
|
if (dev_info->rx_offload_capa & RTE_ETH_RX_OFFLOAD_RSS_HASH) {
|
|
|
|
|
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){
|
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
2 years ago
|
|
|
.rss_key = RSS_HKEY,
|
|
|
|
|
.rss_key_len = RSS_HKEY_LEN,
|
|
|
|
|
.rss_hf = iconf->rss_hf,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const char *dev_driver = dev_info->driver_name;
|
|
|
|
|
if (strcmp(dev_info->driver_name, "net_bonding") == 0) {
|
|
|
|
|
dev_driver = BondingDeviceDriverGet(iconf->port_id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DeviceSetPMDSpecificRSS(&port_conf->rx_adv_conf.rss_conf, dev_driver);
|
|
|
|
|
|
|
|
|
|
uint64_t rss_hf_tmp =
|
|
|
|
|
port_conf->rx_adv_conf.rss_conf.rss_hf & dev_info->flow_type_rss_offloads;
|
|
|
|
|
if (port_conf->rx_adv_conf.rss_conf.rss_hf != rss_hf_tmp) {
|
|
|
|
|
DumpRSSFlags(port_conf->rx_adv_conf.rss_conf.rss_hf, rss_hf_tmp);
|
|
|
|
|
|
|
|
|
|
SCLogWarning("%s: modified RSS hash function based on hardware support: "
|
|
|
|
|
"requested:%#" PRIx64 ", configured:%#" PRIx64,
|
|
|
|
|
iconf->iface, port_conf->rx_adv_conf.rss_conf.rss_hf, rss_hf_tmp);
|
|
|
|
|
port_conf->rx_adv_conf.rss_conf.rss_hf = rss_hf_tmp;
|
|
|
|
|
}
|
|
|
|
|
port_conf->rxmode.mq_mode = RTE_ETH_MQ_RX_RSS;
|
|
|
|
|
} else {
|
|
|
|
|
SCLogConfig("%s: RSS not enabled", iconf->iface);
|
|
|
|
|
port_conf->rx_adv_conf.rss_conf.rss_key = NULL;
|
|
|
|
|
port_conf->rx_adv_conf.rss_conf.rss_hf = 0;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
SCLogConfig("%s: RSS not supported", iconf->iface);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void PortConfSetChsumOffload(const DPDKIfaceConfig *iconf,
|
|
|
|
|
const struct rte_eth_dev_info *dev_info, struct rte_eth_conf *port_conf)
|
|
|
|
|
{
|
|
|
|
|
if (iconf->checksum_mode == CHECKSUM_VALIDATION_DISABLE) {
|
|
|
|
|
SCLogConfig("%s: checksum validation disabled", iconf->iface);
|
|
|
|
|
} else if ((dev_info->rx_offload_capa & RTE_ETH_RX_OFFLOAD_CHECKSUM) ==
|
|
|
|
|
RTE_ETH_RX_OFFLOAD_CHECKSUM) { // multibit comparison to make sure all bits are set
|
|
|
|
|
if (iconf->checksum_mode == CHECKSUM_VALIDATION_ENABLE &&
|
|
|
|
|
iconf->flags & DPDK_RX_CHECKSUM_OFFLOAD) {
|
|
|
|
|
SCLogConfig("%s: IP, TCP and UDP checksum validation offloaded", iconf->iface);
|
|
|
|
|
port_conf->rxmode.offloads |= RTE_ETH_RX_OFFLOAD_CHECKSUM;
|
|
|
|
|
} else if (iconf->checksum_mode == CHECKSUM_VALIDATION_ENABLE &&
|
|
|
|
|
!(iconf->flags & DPDK_RX_CHECKSUM_OFFLOAD)) {
|
|
|
|
|
SCLogConfig("%s: checksum validation enabled (but can be offloaded)", iconf->iface);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void PortConfSetVlanOffload(const DPDKIfaceConfig *iconf,
|
|
|
|
|
const struct rte_eth_dev_info *dev_info, struct rte_eth_conf *port_conf)
|
|
|
|
|
{
|
|
|
|
|
if (iconf->vlan_strip_enabled) {
|
|
|
|
|
if (dev_info->rx_offload_capa & RTE_ETH_RX_OFFLOAD_VLAN_STRIP) {
|
|
|
|
|
port_conf->rxmode.offloads |= RTE_ETH_RX_OFFLOAD_VLAN_STRIP;
|
|
|
|
|
SCLogConfig("%s: hardware VLAN stripping enabled", iconf->iface);
|
|
|
|
|
} else {
|
|
|
|
|
SCLogWarning("%s: hardware VLAN stripping enabled but not supported, disabling",
|
|
|
|
|
iconf->iface);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void DeviceInitPortConf(const DPDKIfaceConfig *iconf,
|
|
|
|
|
const struct rte_eth_dev_info *dev_info, struct rte_eth_conf *port_conf)
|
|
|
|
|
{
|
|
|
|
|
DumpRXOffloadCapabilities(dev_info->rx_offload_capa);
|
|
|
|
|
*port_conf = (struct rte_eth_conf){
|
|
|
|
|
.rxmode = {
|
|
|
|
|
.mq_mode = RTE_ETH_MQ_RX_NONE,
|
|
|
|
|
.offloads = 0, // turn every offload off to prevent any packet modification
|
|
|
|
|
},
|
|
|
|
|
.txmode = {
|
|
|
|
|
.mq_mode = RTE_ETH_MQ_TX_NONE,
|
|
|
|
|
.offloads = 0,
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
PortConfSetInterruptMode(iconf, port_conf);
|
|
|
|
|
|
|
|
|
|
// configure RX offloads
|
|
|
|
|
PortConfSetRSSConf(iconf, dev_info, port_conf);
|
|
|
|
|
PortConfSetChsumOffload(iconf, dev_info, port_conf);
|
|
|
|
|
DeviceSetMTU(port_conf, iconf->mtu);
|
|
|
|
|
PortConfSetVlanOffload(iconf, dev_info, port_conf);
|
|
|
|
|
|
|
|
|
|
if (dev_info->tx_offload_capa & RTE_ETH_TX_OFFLOAD_MBUF_FAST_FREE) {
|
|
|
|
|
port_conf->txmode.offloads |= RTE_ETH_TX_OFFLOAD_MBUF_FAST_FREE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int DeviceConfigureQueues(DPDKIfaceConfig *iconf, const struct rte_eth_dev_info *dev_info,
|
|
|
|
|
const struct rte_eth_conf *port_conf)
|
|
|
|
|
{
|
|
|
|
|
SCEnter();
|
|
|
|
|
int retval;
|
|
|
|
|
struct rte_eth_rxconf rxq_conf;
|
|
|
|
|
struct rte_eth_txconf txq_conf;
|
|
|
|
|
|
|
|
|
|
retval = DPDKDeviceResourcesInit(&(iconf->pkt_mempools), iconf->nb_rx_queues);
|
|
|
|
|
if (retval < 0) {
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// +4 for VLAN header
|
|
|
|
|
uint16_t mtu_size = iconf->mtu + RTE_ETHER_CRC_LEN + RTE_ETHER_HDR_LEN + 4;
|
|
|
|
|
uint16_t mbuf_size = ROUNDUP(mtu_size, 1024) + RTE_PKTMBUF_HEADROOM;
|
|
|
|
|
// Follows DPDK recommendation of having a mempool size that is a power of 2 minus one.
|
|
|
|
|
// So e.g. mp_size of 262144 and 262143 both lead to 65535 on 4 rx queues
|
|
|
|
|
uint32_t raw = iconf->mempool_size / iconf->nb_rx_queues;
|
|
|
|
|
uint32_t next_p2 = rte_align32pow2(raw + 1);
|
|
|
|
|
uint32_t q_mp_sz = (next_p2 == raw + 1) ? raw : (next_p2 >> 1) - 1;
|
|
|
|
|
uint32_t q_mp_cache_sz = iconf->mempool_cache_size_auto ? MempoolCacheSizeCalculate(q_mp_sz)
|
|
|
|
|
: iconf->mempool_cache_size;
|
|
|
|
|
SCLogInfo("%s: creating %u packet mempools of size %u, cache size %u, mbuf size %u",
|
|
|
|
|
iconf->iface, iconf->nb_rx_queues, q_mp_sz, q_mp_cache_sz, mbuf_size);
|
|
|
|
|
for (int i = 0; i < iconf->nb_rx_queues; i++) {
|
|
|
|
|
char mempool_name[64];
|
|
|
|
|
snprintf(mempool_name, sizeof(mempool_name), "mp_%d_%.20s", i, iconf->iface);
|
|
|
|
|
iconf->pkt_mempools->pkt_mp[i] = rte_pktmbuf_pool_create(
|
|
|
|
|
mempool_name, q_mp_sz, q_mp_cache_sz, 0, mbuf_size, (int)iconf->socket_id);
|
|
|
|
|
if (iconf->pkt_mempools->pkt_mp[i] == NULL) {
|
|
|
|
|
retval = -rte_errno;
|
|
|
|
|
SCLogError("%s: rte_pktmbuf_pool_create failed with code %d (mempool: %s) - %s",
|
|
|
|
|
iconf->iface, rte_errno, mempool_name, rte_strerror(rte_errno));
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (uint16_t queue_id = 0; queue_id < iconf->nb_rx_queues; queue_id++) {
|
|
|
|
|
rxq_conf = dev_info->default_rxconf;
|
|
|
|
|
rxq_conf.offloads = port_conf->rxmode.offloads;
|
|
|
|
|
rxq_conf.rx_thresh.hthresh = 0;
|
|
|
|
|
rxq_conf.rx_thresh.pthresh = 0;
|
|
|
|
|
rxq_conf.rx_thresh.wthresh = 0;
|
|
|
|
|
rxq_conf.rx_free_thresh = 0;
|
|
|
|
|
rxq_conf.rx_drop_en = 0;
|
|
|
|
|
SCLogConfig("%s: setting up RX queue %d: rx_desc: %u offloads: 0x%" PRIx64
|
|
|
|
|
" hthresh: %" PRIu8 " pthresh: %" PRIu8 " wthresh: %" PRIu8
|
|
|
|
|
" free_thresh: %" PRIu16 " drop_en: %" PRIu8,
|
|
|
|
|
iconf->iface, queue_id, iconf->nb_rx_desc, rxq_conf.offloads,
|
|
|
|
|
rxq_conf.rx_thresh.hthresh, rxq_conf.rx_thresh.pthresh, rxq_conf.rx_thresh.wthresh,
|
|
|
|
|
rxq_conf.rx_free_thresh, rxq_conf.rx_drop_en);
|
|
|
|
|
|
|
|
|
|
retval = rte_eth_rx_queue_setup(iconf->port_id, queue_id, iconf->nb_rx_desc,
|
|
|
|
|
(unsigned int)iconf->socket_id, &rxq_conf, iconf->pkt_mempools->pkt_mp[queue_id]);
|
|
|
|
|
if (retval < 0) {
|
|
|
|
|
SCLogError("%s: failed to setup RX queue %u: %s", iconf->iface, queue_id,
|
|
|
|
|
rte_strerror(-retval));
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (uint16_t queue_id = 0; queue_id < iconf->nb_tx_queues; queue_id++) {
|
|
|
|
|
txq_conf = dev_info->default_txconf;
|
|
|
|
|
txq_conf.offloads = port_conf->txmode.offloads;
|
|
|
|
|
SCLogConfig("%s: setting up TX queue %d: tx_desc: %" PRIu16 " tx: offloads: 0x%" PRIx64
|
|
|
|
|
" hthresh: %" PRIu8 " pthresh: %" PRIu8 " wthresh: %" PRIu8
|
|
|
|
|
" tx_free_thresh: %" PRIu16 " tx_rs_thresh: %" PRIu16
|
|
|
|
|
" txq_deferred_start: %" PRIu8,
|
|
|
|
|
iconf->iface, queue_id, iconf->nb_tx_desc, txq_conf.offloads,
|
|
|
|
|
txq_conf.tx_thresh.hthresh, txq_conf.tx_thresh.pthresh, txq_conf.tx_thresh.wthresh,
|
|
|
|
|
txq_conf.tx_free_thresh, txq_conf.tx_rs_thresh, txq_conf.tx_deferred_start);
|
|
|
|
|
retval = rte_eth_tx_queue_setup(iconf->port_id, queue_id, iconf->nb_tx_desc,
|
|
|
|
|
(unsigned int)iconf->socket_id, &txq_conf);
|
|
|
|
|
if (retval < 0) {
|
|
|
|
|
SCLogError("%s: failed to setup TX queue %u: %s", iconf->iface, queue_id,
|
|
|
|
|
rte_strerror(-retval));
|
|
|
|
|
retval = -1; // the error code explained, informing about failure
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SCReturnInt(0);
|
|
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
|
DPDKDeviceResourcesDeinit(&iconf->pkt_mempools);
|
|
|
|
|
SCReturnInt(retval);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int DeviceValidateOutIfaceConfig(DPDKIfaceConfig *iconf)
|
|
|
|
|
{
|
|
|
|
|
SCEnter();
|
|
|
|
|
int retval;
|
|
|
|
|
DPDKIfaceConfig *out_iconf = NULL;
|
|
|
|
|
ConfigInit(&out_iconf);
|
|
|
|
|
if (out_iconf == NULL) {
|
|
|
|
|
FatalError("Copy interface of the interface \"%s\" is NULL", iconf->iface);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
retval = ConfigLoad(out_iconf, iconf->out_iface);
|
|
|
|
|
if (retval < 0) {
|
|
|
|
|
SCLogError("%s: fail to load config of interface", iconf->out_iface);
|
|
|
|
|
out_iconf->DerefFunc(out_iconf);
|
|
|
|
|
SCReturnInt(-EINVAL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (iconf->nb_rx_queues != out_iconf->nb_tx_queues) {
|
|
|
|
|
// the other direction is validated when the copy interface is configured
|
|
|
|
|
SCLogError("%s: configured %d RX queues but copy interface %s has %d TX queues"
|
|
|
|
|
" - number of queues must be equal",
|
|
|
|
|
iconf->iface, iconf->nb_rx_queues, out_iconf->iface, out_iconf->nb_tx_queues);
|
|
|
|
|
out_iconf->DerefFunc(out_iconf);
|
|
|
|
|
SCReturnInt(-EINVAL);
|
|
|
|
|
} else if (iconf->mtu != out_iconf->mtu) {
|
|
|
|
|
SCLogError("%s: configured MTU of %d but copy interface %s has MTU set to %d"
|
|
|
|
|
" - MTU must be equal",
|
|
|
|
|
iconf->iface, iconf->mtu, out_iconf->iface, out_iconf->mtu);
|
|
|
|
|
out_iconf->DerefFunc(out_iconf);
|
|
|
|
|
SCReturnInt(-EINVAL);
|
|
|
|
|
} else if (iconf->copy_mode != out_iconf->copy_mode) {
|
|
|
|
|
SCLogError("%s: copy modes of interfaces %s and %s are not equal", iconf->iface,
|
|
|
|
|
iconf->iface, out_iconf->iface);
|
|
|
|
|
out_iconf->DerefFunc(out_iconf);
|
|
|
|
|
SCReturnInt(-EINVAL);
|
|
|
|
|
} else if (strcmp(iconf->iface, out_iconf->out_iface) != 0) {
|
|
|
|
|
// check if the other iface has the current iface set as a copy iface
|
|
|
|
|
SCLogError("%s: copy interface of %s is not set to %s", iconf->iface, out_iconf->iface,
|
|
|
|
|
iconf->iface);
|
|
|
|
|
out_iconf->DerefFunc(out_iconf);
|
|
|
|
|
SCReturnInt(-EINVAL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
out_iconf->DerefFunc(out_iconf);
|
|
|
|
|
SCReturnInt(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int DeviceConfigureIPS(DPDKIfaceConfig *iconf)
|
|
|
|
|
{
|
|
|
|
|
SCEnter();
|
|
|
|
|
if (iconf->out_iface != NULL) {
|
|
|
|
|
if (!rte_eth_dev_is_valid_port(iconf->out_port_id)) {
|
|
|
|
|
SCLogError("%s: retrieved copy interface port ID \"%d\" is invalid or the device is "
|
|
|
|
|
"not attached ",
|
|
|
|
|
iconf->iface, iconf->out_port_id);
|
|
|
|
|
SCReturnInt(-ENODEV);
|
|
|
|
|
}
|
|
|
|
|
int32_t out_port_socket_id;
|
|
|
|
|
int retval = DPDKDeviceSetSocketID(iconf->out_port_id, &out_port_socket_id);
|
|
|
|
|
if (retval < 0) {
|
|
|
|
|
SCLogError("%s: invalid socket id: %s", iconf->out_iface, rte_strerror(-retval));
|
|
|
|
|
SCReturnInt(retval);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (iconf->socket_id != out_port_socket_id) {
|
|
|
|
|
SCLogWarning(
|
|
|
|
|
"%s: out iface %s is not on the same NUMA node (%s - NUMA %d, %s - NUMA %d)",
|
|
|
|
|
iconf->iface, iconf->out_iface, iconf->iface, iconf->socket_id,
|
|
|
|
|
iconf->out_iface, out_port_socket_id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
retval = DeviceValidateOutIfaceConfig(iconf);
|
|
|
|
|
if (retval != 0) {
|
|
|
|
|
// Error will be written out by the validation function
|
|
|
|
|
SCReturnInt(retval);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (iconf->copy_mode == DPDK_COPY_MODE_IPS)
|
|
|
|
|
SCLogInfo("%s: DPDK IPS mode activated: %s->%s", iconf->iface, iconf->iface,
|
|
|
|
|
iconf->out_iface);
|
|
|
|
|
else if (iconf->copy_mode == DPDK_COPY_MODE_TAP)
|
|
|
|
|
SCLogInfo("%s: DPDK TAP mode activated: %s->%s", iconf->iface, iconf->iface,
|
|
|
|
|
iconf->out_iface);
|
|
|
|
|
}
|
|
|
|
|
SCReturnInt(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Function verifies changes in e.g. device info after configuration has
|
|
|
|
|
* happened. Sometimes (e.g. DPDK Bond PMD with Intel NICs i40e/ixgbe) change
|
|
|
|
|
* device info only after the device configuration.
|
|
|
|
|
* @param iconf
|
|
|
|
|
* @param dev_info
|
|
|
|
|
* @return 0 on success, -EAGAIN when reconfiguration is needed, <0 on failure
|
|
|
|
|
*/
|
|
|
|
|
static int32_t DeviceVerifyPostConfigure(
|
|
|
|
|
const DPDKIfaceConfig *iconf, const struct rte_eth_dev_info *dev_info)
|
|
|
|
|
{
|
|
|
|
|
SCEnter();
|
|
|
|
|
struct rte_eth_dev_info post_conf_dev_info = { 0 };
|
|
|
|
|
int32_t ret = rte_eth_dev_info_get(iconf->port_id, &post_conf_dev_info);
|
|
|
|
|
if (ret < 0) {
|
|
|
|
|
SCLogError("%s: getting device info failed: %s", iconf->iface, rte_strerror(-ret));
|
|
|
|
|
SCReturnInt(ret);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (dev_info->flow_type_rss_offloads != post_conf_dev_info.flow_type_rss_offloads ||
|
|
|
|
|
dev_info->rx_offload_capa != post_conf_dev_info.rx_offload_capa ||
|
|
|
|
|
dev_info->tx_offload_capa != post_conf_dev_info.tx_offload_capa ||
|
|
|
|
|
dev_info->max_rx_queues != post_conf_dev_info.max_rx_queues ||
|
|
|
|
|
dev_info->max_tx_queues != post_conf_dev_info.max_tx_queues ||
|
|
|
|
|
dev_info->max_mtu != post_conf_dev_info.max_mtu) {
|
|
|
|
|
SCLogWarning("%s: device information severely changed after configuration, reconfiguring",
|
|
|
|
|
iconf->iface);
|
|
|
|
|
return -EAGAIN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (strcmp(dev_info->driver_name, "net_bonding") == 0) {
|
|
|
|
|
ret = BondingAllDevicesSameDriver(iconf->port_id);
|
|
|
|
|
if (ret < 0) {
|
|
|
|
|
SCLogError("%s: bond port uses port with different DPDK drivers", iconf->iface);
|
|
|
|
|
SCReturnInt(ret);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int DeviceConfigure(DPDKIfaceConfig *iconf)
|
|
|
|
|
{
|
|
|
|
|
SCEnter();
|
|
|
|
|
if (!rte_eth_dev_is_valid_port(iconf->port_id)) {
|
|
|
|
|
SCLogError("%s: retrieved port ID \"%d\" is invalid or the device is not attached ",
|
|
|
|
|
iconf->iface, iconf->port_id);
|
|
|
|
|
SCReturnInt(-ENODEV);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int32_t retval = DPDKDeviceSetSocketID(iconf->port_id, &iconf->socket_id);
|
|
|
|
|
if (retval < 0) {
|
|
|
|
|
SCLogError("%s: invalid socket id: %s", iconf->iface, rte_strerror(-retval));
|
|
|
|
|
SCReturnInt(retval);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct rte_eth_dev_info dev_info = { 0 };
|
|
|
|
|
retval = rte_eth_dev_info_get(iconf->port_id, &dev_info);
|
|
|
|
|
if (retval < 0) {
|
|
|
|
|
SCLogError("%s: getting device info failed: %s", iconf->iface, rte_strerror(-retval));
|
|
|
|
|
SCReturnInt(retval);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (iconf->nb_rx_queues > dev_info.max_rx_queues) {
|
|
|
|
|
SCLogError("%s: configured RX queues %u is higher than device maximum (%" PRIu16 ")",
|
|
|
|
|
iconf->iface, iconf->nb_rx_queues, dev_info.max_rx_queues);
|
|
|
|
|
SCReturnInt(-ERANGE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (iconf->nb_tx_queues > dev_info.max_tx_queues) {
|
|
|
|
|
SCLogError("%s: configured TX queues %u is higher than device maximum (%" PRIu16 ")",
|
|
|
|
|
iconf->iface, iconf->nb_tx_queues, dev_info.max_tx_queues);
|
|
|
|
|
SCReturnInt(-ERANGE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
retval = DeviceValidateMTU(iconf, &dev_info);
|
|
|
|
|
if (retval < 0)
|
|
|
|
|
return retval;
|
|
|
|
|
|
|
|
|
|
struct rte_eth_conf port_conf = { 0 };
|
|
|
|
|
DeviceInitPortConf(iconf, &dev_info, &port_conf);
|
|
|
|
|
if (port_conf.rxmode.offloads & RTE_ETH_RX_OFFLOAD_CHECKSUM) {
|
|
|
|
|
// Suricata does not need recalc checksums now
|
|
|
|
|
iconf->checksum_mode = CHECKSUM_VALIDATION_OFFLOAD;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
retval = rte_eth_dev_configure(
|
|
|
|
|
iconf->port_id, iconf->nb_rx_queues, iconf->nb_tx_queues, &port_conf);
|
|
|
|
|
if (retval < 0) {
|
|
|
|
|
SCLogError("%s: failed to configure the device: %s", iconf->iface, rte_strerror(-retval));
|
|
|
|
|
SCReturnInt(retval);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
retval = DeviceVerifyPostConfigure(iconf, &dev_info);
|
|
|
|
|
if (retval < 0)
|
|
|
|
|
return retval;
|
|
|
|
|
|
|
|
|
|
uint16_t tmp_nb_rx_desc = iconf->nb_rx_desc;
|
|
|
|
|
uint16_t tmp_nb_tx_desc = iconf->nb_tx_desc;
|
|
|
|
|
retval = rte_eth_dev_adjust_nb_rx_tx_desc(
|
|
|
|
|
iconf->port_id, &iconf->nb_rx_desc, &iconf->nb_tx_desc);
|
|
|
|
|
if (retval != 0) {
|
|
|
|
|
SCLogError("%s: failed to adjust device queue descriptors: %s", iconf->iface,
|
|
|
|
|
rte_strerror(-retval));
|
|
|
|
|
SCReturnInt(retval);
|
|
|
|
|
} else if (tmp_nb_rx_desc != iconf->nb_rx_desc || tmp_nb_tx_desc != iconf->nb_tx_desc) {
|
|
|
|
|
SCLogWarning("%s: device queue descriptors adjusted (RX: from %u to %u, TX: from %u to %u)",
|
|
|
|
|
iconf->iface, tmp_nb_rx_desc, iconf->nb_rx_desc, tmp_nb_tx_desc, iconf->nb_tx_desc);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
retval = iconf->flags & DPDK_MULTICAST ? rte_eth_allmulticast_enable(iconf->port_id)
|
|
|
|
|
: rte_eth_allmulticast_disable(iconf->port_id);
|
|
|
|
|
if (retval == -ENOTSUP) {
|
|
|
|
|
retval = rte_eth_allmulticast_get(iconf->port_id);
|
|
|
|
|
// when multicast is enabled but set to disable or vice versa
|
|
|
|
|
if ((retval == 1 && !(iconf->flags & DPDK_MULTICAST)) ||
|
|
|
|
|
(retval == 0 && (iconf->flags & DPDK_MULTICAST))) {
|
|
|
|
|
SCLogWarning("%s: cannot configure allmulticast, the port is %sin allmulticast mode",
|
|
|
|
|
iconf->iface, retval == 1 ? "" : "not ");
|
|
|
|
|
} else if (retval < 0) {
|
|
|
|
|
SCLogError("%s: failed to get multicast mode: %s", iconf->iface, rte_strerror(-retval));
|
|
|
|
|
SCReturnInt(retval);
|
|
|
|
|
}
|
|
|
|
|
} else if (retval < 0) {
|
|
|
|
|
SCLogError("%s: error when changing multicast setting: %s", iconf->iface,
|
|
|
|
|
rte_strerror(-retval));
|
|
|
|
|
SCReturnInt(retval);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
retval = iconf->flags & DPDK_PROMISC ? rte_eth_promiscuous_enable(iconf->port_id)
|
|
|
|
|
: rte_eth_promiscuous_disable(iconf->port_id);
|
|
|
|
|
if (retval == -ENOTSUP) {
|
|
|
|
|
retval = rte_eth_promiscuous_get(iconf->port_id);
|
|
|
|
|
if ((retval == 1 && !(iconf->flags & DPDK_PROMISC)) ||
|
|
|
|
|
(retval == 0 && (iconf->flags & DPDK_PROMISC))) {
|
|
|
|
|
SCLogError("%s: cannot configure promiscuous mode, the port is in %spromiscuous mode",
|
|
|
|
|
iconf->iface, retval == 1 ? "" : "non-");
|
|
|
|
|
SCReturnInt(TM_ECODE_FAILED);
|
|
|
|
|
} else if (retval < 0) {
|
|
|
|
|
SCLogError(
|
|
|
|
|
"%s: failed to get promiscuous mode: %s", iconf->iface, rte_strerror(-retval));
|
|
|
|
|
SCReturnInt(retval);
|
|
|
|
|
}
|
|
|
|
|
} else if (retval < 0) {
|
|
|
|
|
SCLogError("%s: error when changing promiscuous setting: %s", iconf->iface,
|
|
|
|
|
rte_strerror(-retval));
|
|
|
|
|
SCReturnInt(TM_ECODE_FAILED);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// set maximum transmission unit
|
|
|
|
|
SCLogConfig("%s: setting MTU to %d", iconf->iface, iconf->mtu);
|
|
|
|
|
retval = rte_eth_dev_set_mtu(iconf->port_id, iconf->mtu);
|
|
|
|
|
if (retval == -ENOTSUP) {
|
|
|
|
|
// if it is not possible to set the MTU, retrieve it
|
|
|
|
|
retval = rte_eth_dev_get_mtu(iconf->port_id, &iconf->mtu);
|
|
|
|
|
if (retval < 0) {
|
|
|
|
|
SCLogError("%s: failed to retrieve MTU: %s", iconf->iface, rte_strerror(-retval));
|
|
|
|
|
SCReturnInt(retval);
|
|
|
|
|
}
|
|
|
|
|
SCLogWarning(
|
|
|
|
|
"%s: changing MTU is not supported, current MTU: %u", iconf->iface, iconf->mtu);
|
|
|
|
|
} else if (retval < 0) {
|
|
|
|
|
SCLogError(
|
|
|
|
|
"%s: failed to set MTU to %u: %s", iconf->iface, iconf->mtu, rte_strerror(-retval));
|
|
|
|
|
SCReturnInt(retval);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
retval = DeviceConfigureQueues(iconf, &dev_info, &port_conf);
|
|
|
|
|
if (retval < 0) {
|
|
|
|
|
SCReturnInt(retval);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
retval = DeviceConfigureIPS(iconf);
|
|
|
|
|
if (retval < 0) {
|
|
|
|
|
SCReturnInt(retval);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SCReturnInt(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void *ParseDpdkConfigAndConfigureDevice(const char *iface)
|
|
|
|
|
{
|
|
|
|
|
int retval;
|
|
|
|
|
DPDKIfaceConfig *iconf = ConfigParse(iface);
|
|
|
|
|
if (iconf == NULL) {
|
|
|
|
|
FatalError("DPDK configuration could not be parsed");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
retval = DeviceConfigure(iconf);
|
|
|
|
|
if (retval == -EAGAIN) {
|
|
|
|
|
// for e.g. bonding PMD it needs to be reconfigured
|
|
|
|
|
retval = DeviceConfigure(iconf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (retval < 0) { // handles both configure attempts
|
|
|
|
|
iconf->DerefFunc(iconf);
|
|
|
|
|
if (rte_eal_cleanup() != 0)
|
|
|
|
|
FatalError("EAL cleanup failed: %s", rte_strerror(-retval));
|
|
|
|
|
|
|
|
|
|
if (retval == -ENOMEM) {
|
|
|
|
|
FatalError("%s: memory allocation failed - consider"
|
|
|
|
|
"%s freeing up some memory.",
|
|
|
|
|
iface,
|
|
|
|
|
rte_eal_has_hugepages() != 0 ? " increasing the number of hugepages or" : "");
|
|
|
|
|
} else {
|
|
|
|
|
FatalError("%s: failed to configure", iface);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SC_ATOMIC_RESET(iconf->ref);
|
|
|
|
|
(void)SC_ATOMIC_ADD(iconf->ref, iconf->threads);
|
|
|
|
|
// This counter is increased by worker threads that individually pick queue IDs.
|
|
|
|
|
SC_ATOMIC_RESET(iconf->queue_id);
|
|
|
|
|
SC_ATOMIC_RESET(iconf->inconsistent_numa_cnt);
|
|
|
|
|
iconf->workers_sync = SCCalloc(1, sizeof(*iconf->workers_sync));
|
|
|
|
|
if (iconf->workers_sync == NULL) {
|
|
|
|
|
FatalError("Failed to allocate memory for workers_sync");
|
|
|
|
|
}
|
|
|
|
|
SC_ATOMIC_RESET(iconf->workers_sync->worker_checked_in);
|
|
|
|
|
iconf->workers_sync->worker_cnt = iconf->threads;
|
|
|
|
|
|
|
|
|
|
// initialize LiveDev DPDK values
|
|
|
|
|
LiveDevice *ldev_instance = LiveGetDevice(iface);
|
|
|
|
|
if (ldev_instance == NULL) {
|
|
|
|
|
FatalError("Device %s is not registered as a live device", iface);
|
|
|
|
|
}
|
|
|
|
|
ldev_instance->dpdk_vars = iconf->pkt_mempools;
|
|
|
|
|
iconf->pkt_mempools = NULL;
|
|
|
|
|
return iconf;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \brief extract information from config file
|
|
|
|
|
*
|
|
|
|
|
* The returned structure will be freed by the thread init function.
|
|
|
|
|
* This is thus necessary to or copy the structure before giving it
|
|
|
|
|
* to thread or to reparse the file for each thread (and thus have
|
|
|
|
|
* new structure.
|
|
|
|
|
*
|
|
|
|
|
* After configuration is loaded, DPDK also configures the device according to the settings.
|
|
|
|
|
*
|
|
|
|
|
* \return a DPDKIfaceConfig corresponding to the interface name
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
static uint16_t DPDKConfigGetThreadsCount(void *conf)
|
|
|
|
|
{
|
|
|
|
|
if (conf == NULL)
|
|
|
|
|
FatalError("Configuration file is NULL");
|
|
|
|
|
|
|
|
|
|
DPDKIfaceConfig *dpdk_conf = (DPDKIfaceConfig *)conf;
|
|
|
|
|
return dpdk_conf->threads;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif /* HAVE_DPDK */
|
|
|
|
|
|
|
|
|
|
static int DPDKRunModeIsIPS(void)
|
|
|
|
|
{
|
|
|
|
|
/* Find initial node */
|
|
|
|
|
const char dpdk_node_query[] = "dpdk.interfaces";
|
|
|
|
|
SCConfNode *dpdk_node = SCConfGetNode(dpdk_node_query);
|
|
|
|
|
if (dpdk_node == NULL) {
|
|
|
|
|
FatalError("Unable to get %s configuration node", dpdk_node_query);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char default_iface[] = "default";
|
|
|
|
|
SCConfNode *if_default = SCConfNodeLookupKeyValue(dpdk_node, "interface", default_iface);
|
|
|
|
|
int nlive = LiveGetDeviceCount();
|
|
|
|
|
bool has_ips = false;
|
|
|
|
|
bool has_ids = false;
|
|
|
|
|
for (int ldev = 0; ldev < nlive; ldev++) {
|
|
|
|
|
const char *live_dev = LiveGetDeviceName(ldev);
|
|
|
|
|
if (live_dev == NULL)
|
|
|
|
|
FatalError("Unable to get device id %d from LiveDevice list", ldev);
|
|
|
|
|
|
|
|
|
|
SCConfNode *if_root = ConfFindDeviceConfig(dpdk_node, live_dev);
|
|
|
|
|
if (if_root == NULL) {
|
|
|
|
|
if (if_default == NULL)
|
|
|
|
|
FatalError("Unable to get %s or %s interface", live_dev, default_iface);
|
|
|
|
|
|
|
|
|
|
if_root = if_default;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *copymodestr = NULL;
|
|
|
|
|
const char *copyifacestr = NULL;
|
|
|
|
|
if (SCConfGetChildValueWithDefault(if_root, if_default, "copy-mode", ©modestr) == 1 &&
|
|
|
|
|
SCConfGetChildValue(if_root, "copy-iface", ©ifacestr) == 1) {
|
|
|
|
|
if (strcmp(copymodestr, "ips") == 0) {
|
|
|
|
|
has_ips = true;
|
|
|
|
|
} else {
|
|
|
|
|
has_ids = true;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
has_ids = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (has_ids && has_ips) {
|
|
|
|
|
FatalError("Copy-mode of interface %s mixes with the previously set copy-modes "
|
|
|
|
|
"(only IDS/TAP and IPS copy-mode combinations are allowed in DPDK",
|
|
|
|
|
live_dev);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return has_ips;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int DPDKRunModeEnableIPS(void)
|
|
|
|
|
{
|
|
|
|
|
int r = DPDKRunModeIsIPS();
|
|
|
|
|
if (r == 1) {
|
|
|
|
|
SCLogInfo("Setting IPS mode");
|
|
|
|
|
EngineModeSetIPS();
|
|
|
|
|
}
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *RunModeDpdkGetDefaultMode(void)
|
|
|
|
|
{
|
|
|
|
|
return "workers";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RunModeDpdkRegister(void)
|
|
|
|
|
{
|
|
|
|
|
RunModeRegisterNewRunMode(RUNMODE_DPDK, "workers",
|
|
|
|
|
"Workers DPDK mode, each thread does all"
|
|
|
|
|
" tasks from acquisition to logging",
|
|
|
|
|
RunModeIdsDpdkWorkers, DPDKRunModeEnableIPS);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \brief Workers version of the DPDK processing.
|
|
|
|
|
*
|
|
|
|
|
* Start N threads with each thread doing all the work.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
int RunModeIdsDpdkWorkers(void)
|
|
|
|
|
{
|
|
|
|
|
SCEnter();
|
|
|
|
|
#ifdef HAVE_DPDK
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
TimeModeSetLive();
|
|
|
|
|
|
|
|
|
|
InitEal();
|
|
|
|
|
ret = RunModeSetLiveCaptureWorkers(ParseDpdkConfigAndConfigureDevice, DPDKConfigGetThreadsCount,
|
|
|
|
|
"ReceiveDPDK", "DecodeDPDK", thread_name_workers, NULL);
|
|
|
|
|
if (ret != 0) {
|
|
|
|
|
FatalError("Unable to start runmode");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SCLogDebug("RunModeIdsDpdkWorkers initialised");
|
|
|
|
|
|
|
|
|
|
#endif /* HAVE_DPDK */
|
|
|
|
|
SCReturnInt(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @}
|
|
|
|
|
*/
|