/src/suricata8/src/runmode-dpdk.c
Line | Count | Source |
1 | | /* Copyright (C) 2021-2025 Open Information Security Foundation |
2 | | * |
3 | | * You can copy, redistribute or modify this Program under the terms of |
4 | | * the GNU General Public License version 2 as published by the Free |
5 | | * Software Foundation. |
6 | | * |
7 | | * This program is distributed in the hope that it will be useful, |
8 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
9 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
10 | | * GNU General Public License for more details. |
11 | | * |
12 | | * You should have received a copy of the GNU General Public License |
13 | | * version 2 along with this program; if not, write to the Free Software |
14 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
15 | | * 02110-1301, USA. |
16 | | */ |
17 | | |
18 | | /** |
19 | | * \ingroup dpdk |
20 | | * |
21 | | * @{ |
22 | | */ |
23 | | |
24 | | /** |
25 | | * \file |
26 | | * |
27 | | * \author Lukas Sismis <lukas.sismis@gmail.com> |
28 | | * |
29 | | * DPDK runmode |
30 | | * |
31 | | */ |
32 | | |
33 | | #include "suricata-common.h" |
34 | | #include "runmodes.h" |
35 | | #include "runmode-dpdk.h" |
36 | | #include "decode.h" |
37 | | #include "source-dpdk.h" |
38 | | #include "util-runmodes.h" |
39 | | #include "util-byte.h" |
40 | | #include "util-cpu.h" |
41 | | #include "util-debug.h" |
42 | | #include "util-device-private.h" |
43 | | #include "util-dpdk.h" |
44 | | #include "util-dpdk-bonding.h" |
45 | | #include "util-dpdk-common.h" |
46 | | #include "util-dpdk-i40e.h" |
47 | | #include "util-dpdk-ice.h" |
48 | | #include "util-dpdk-ixgbe.h" |
49 | | #include "util-dpdk-rss.h" |
50 | | #include "util-time.h" |
51 | | #include "util-conf.h" |
52 | | #include "suricata.h" |
53 | | #include "util-affinity.h" |
54 | | |
55 | | #ifdef HAVE_DPDK |
56 | | |
57 | | // Calculates the closest multiple of y from x |
58 | | #define ROUNDUP(x, y) ((((x) + ((y)-1)) / (y)) * (y)) |
59 | | |
60 | | /* Maximum DPDK EAL parameters count. */ |
61 | | #define EAL_ARGS 48 |
62 | | |
63 | | struct Arguments { |
64 | | uint16_t capacity; |
65 | | char **argv; |
66 | | uint16_t argc; |
67 | | }; |
68 | | |
69 | | static char *AllocArgument(size_t arg_len); |
70 | | static char *AllocAndSetArgument(const char *arg); |
71 | | static char *AllocAndSetOption(const char *arg); |
72 | | |
73 | | static void ArgumentsInit(struct Arguments *args, uint16_t capacity); |
74 | | static void ArgumentsCleanup(struct Arguments *args); |
75 | | static void ArgumentsAdd(struct Arguments *args, char *value); |
76 | | static void ArgumentsAddOptionAndArgument(struct Arguments *args, const char *opt, const char *arg); |
77 | | static void InitEal(void); |
78 | | |
79 | | static void ConfigSetIface(DPDKIfaceConfig *iconf, const char *entry_str); |
80 | | static int ConfigSetThreads(DPDKIfaceConfig *iconf, const char *entry_str); |
81 | | static int ConfigSetRxQueues(DPDKIfaceConfig *iconf, uint16_t nb_queues, uint16_t max_queues); |
82 | | static int ConfigSetTxQueues( |
83 | | DPDKIfaceConfig *iconf, uint16_t nb_queues, uint16_t max_queues, bool iface_sends_pkts); |
84 | | static int ConfigSetMempoolSize( |
85 | | DPDKIfaceConfig *iconf, const char *entry_str, const struct rte_eth_dev_info *dev_info); |
86 | | static int ConfigSetMempoolCacheSize(DPDKIfaceConfig *iconf, const char *entry_str); |
87 | | static int ConfigSetRxDescriptors(DPDKIfaceConfig *iconf, const char *entry_str, uint16_t max_desc); |
88 | | static int ConfigSetTxDescriptors( |
89 | | DPDKIfaceConfig *iconf, const char *entry_str, uint16_t max_desc, bool iface_sends_pkts); |
90 | | static int ConfigSetMtu(DPDKIfaceConfig *iconf, intmax_t entry_int); |
91 | | static bool ConfigSetPromiscuousMode(DPDKIfaceConfig *iconf, int entry_bool); |
92 | | static bool ConfigSetMulticast(DPDKIfaceConfig *iconf, int entry_bool); |
93 | | static int ConfigSetChecksumChecks(DPDKIfaceConfig *iconf, int entry_bool); |
94 | | static int ConfigSetChecksumOffload(DPDKIfaceConfig *iconf, int entry_bool); |
95 | | static int ConfigSetCopyIface(DPDKIfaceConfig *iconf, const char *entry_str); |
96 | | static int ConfigSetCopyMode(DPDKIfaceConfig *iconf, const char *entry_str); |
97 | | static int ConfigSetCopyIfaceSettings(DPDKIfaceConfig *iconf, const char *iface, const char *mode); |
98 | | static void ConfigInit(DPDKIfaceConfig **iconf); |
99 | | static int ConfigLoad(DPDKIfaceConfig *iconf, const char *iface); |
100 | | static DPDKIfaceConfig *ConfigParse(const char *iface); |
101 | | |
102 | | static void DeviceInitPortConf(const DPDKIfaceConfig *iconf, |
103 | | const struct rte_eth_dev_info *dev_info, struct rte_eth_conf *port_conf); |
104 | | static int DeviceConfigureQueues(DPDKIfaceConfig *iconf, const struct rte_eth_dev_info *dev_info, |
105 | | const struct rte_eth_conf *port_conf); |
106 | | static int DeviceValidateOutIfaceConfig(DPDKIfaceConfig *iconf); |
107 | | static int DeviceConfigureIPS(DPDKIfaceConfig *iconf); |
108 | | static int DeviceConfigure(DPDKIfaceConfig *iconf); |
109 | | static void *ParseDpdkConfigAndConfigureDevice(const char *iface); |
110 | | static void DPDKDerefConfig(void *conf); |
111 | | |
112 | | #define DPDK_CONFIG_DEFAULT_THREADS "auto" |
113 | | #define DPDK_CONFIG_DEFAULT_INTERRUPT_MODE false |
114 | | #define DPDK_CONFIG_DEFAULT_MEMPOOL_SIZE "auto" |
115 | | #define DPDK_CONFIG_DEFAULT_MEMPOOL_CACHE_SIZE "auto" |
116 | | #define DPDK_CONFIG_DEFAULT_RX_DESCRIPTORS "auto" |
117 | | #define DPDK_CONFIG_DEFAULT_TX_DESCRIPTORS "auto" |
118 | | #define DPDK_CONFIG_DEFAULT_RSS_HASH_FUNCTIONS RTE_ETH_RSS_IP |
119 | | #define DPDK_CONFIG_DEFAULT_MTU 1500 |
120 | | #define DPDK_CONFIG_DEFAULT_PROMISCUOUS_MODE 1 |
121 | | #define DPDK_CONFIG_DEFAULT_MULTICAST_MODE 1 |
122 | | #define DPDK_CONFIG_DEFAULT_CHECKSUM_VALIDATION 1 |
123 | | #define DPDK_CONFIG_DEFAULT_CHECKSUM_VALIDATION_OFFLOAD 1 |
124 | | #define DPDK_CONFIG_DEFAULT_VLAN_STRIP 0 |
125 | | #define DPDK_CONFIG_DEFAULT_LINKUP_TIMEOUT 0 |
126 | | #define DPDK_CONFIG_DEFAULT_COPY_MODE "none" |
127 | | #define DPDK_CONFIG_DEFAULT_COPY_INTERFACE "none" |
128 | | |
129 | | DPDKIfaceConfigAttributes dpdk_yaml = { |
130 | | .threads = "threads", |
131 | | .irq_mode = "interrupt-mode", |
132 | | .promisc = "promisc", |
133 | | .multicast = "multicast", |
134 | | .checksum_checks = "checksum-checks", |
135 | | .checksum_checks_offload = "checksum-checks-offload", |
136 | | .mtu = "mtu", |
137 | | .vlan_strip_offload = "vlan-strip-offload", |
138 | | .rss_hf = "rss-hash-functions", |
139 | | .linkup_timeout = "linkup-timeout", |
140 | | .mempool_size = "mempool-size", |
141 | | .mempool_cache_size = "mempool-cache-size", |
142 | | .rx_descriptors = "rx-descriptors", |
143 | | .tx_descriptors = "tx-descriptors", |
144 | | .copy_mode = "copy-mode", |
145 | | .copy_iface = "copy-iface", |
146 | | }; |
147 | | |
148 | | /** |
149 | | * \brief Input is a number of which we want to find the greatest divisor up to max_num (inclusive). |
150 | | * The divisor is returned. |
151 | | */ |
152 | | static uint32_t GreatestDivisorUpTo(uint32_t num, uint32_t max_num) |
153 | | { |
154 | | for (uint32_t i = max_num; i >= 2; i--) { |
155 | | if (num % i == 0) { |
156 | | return i; |
157 | | } |
158 | | } |
159 | | return 1; |
160 | | } |
161 | | |
162 | | /** |
163 | | * \brief Input is a number of which we want to find the greatest power of 2 up to num. The power of |
164 | | * 2 is returned or 0 if no valid power of 2 is found. |
165 | | */ |
166 | | static uint64_t GreatestPowOf2UpTo(uint64_t num) |
167 | | { |
168 | | if (num == 0) { |
169 | | return 0; // No power of 2 exists for 0 |
170 | | } |
171 | | |
172 | | // Bit manipulation to isolate the highest set bit |
173 | | num |= (num >> 1); |
174 | | num |= (num >> 2); |
175 | | num |= (num >> 4); |
176 | | num |= (num >> 8); |
177 | | num |= (num >> 16); |
178 | | num |= (num >> 32); |
179 | | num = num - (num >> 1); |
180 | | |
181 | | return num; |
182 | | } |
183 | | |
184 | | static char *AllocArgument(size_t arg_len) |
185 | | { |
186 | | SCEnter(); |
187 | | char *ptr; |
188 | | |
189 | | arg_len += 1; // null character |
190 | | ptr = (char *)SCCalloc(arg_len, sizeof(char)); |
191 | | if (ptr == NULL) |
192 | | FatalError("Could not allocate memory for an argument"); |
193 | | |
194 | | SCReturnPtr(ptr, "char *"); |
195 | | } |
196 | | |
197 | | /** |
198 | | * Allocates space for length of the given string and then copies contents |
199 | | * @param arg String to set to the newly allocated space |
200 | | * @return memory address if no error otherwise NULL (with errno set) |
201 | | */ |
202 | | static char *AllocAndSetArgument(const char *arg) |
203 | | { |
204 | | SCEnter(); |
205 | | if (arg == NULL) |
206 | | FatalError("Passed argument is NULL in DPDK config initialization"); |
207 | | |
208 | | char *ptr; |
209 | | size_t arg_len = strlen(arg); |
210 | | |
211 | | ptr = AllocArgument(arg_len); |
212 | | strlcpy(ptr, arg, arg_len + 1); |
213 | | SCReturnPtr(ptr, "char *"); |
214 | | } |
215 | | |
216 | | static char *AllocAndSetOption(const char *arg) |
217 | | { |
218 | | SCEnter(); |
219 | | if (arg == NULL) |
220 | | FatalError("Passed option is NULL in DPDK config initialization"); |
221 | | |
222 | | char *ptr = NULL; |
223 | | size_t arg_len = strlen(arg); |
224 | | uint8_t is_long_arg = arg_len > 1; |
225 | | const char *dash_prefix = is_long_arg ? "--" : "-"; |
226 | | size_t full_len = arg_len + strlen(dash_prefix); |
227 | | |
228 | | ptr = AllocArgument(full_len); |
229 | | strlcpy(ptr, dash_prefix, full_len); |
230 | | strlcat(ptr, arg, full_len + 1); |
231 | | SCReturnPtr(ptr, "char *"); |
232 | | } |
233 | | |
234 | | static void ArgumentsInit(struct Arguments *args, uint16_t capacity) |
235 | | { |
236 | | SCEnter(); |
237 | | args->argv = SCCalloc(capacity, sizeof(*args->argv)); // alloc array of pointers |
238 | | if (args->argv == NULL) |
239 | | FatalError("Could not allocate memory for Arguments structure"); |
240 | | |
241 | | args->capacity = capacity; |
242 | | args->argc = 0; |
243 | | SCReturn; |
244 | | } |
245 | | |
246 | | static void ArgumentsCleanup(struct Arguments *args) |
247 | | { |
248 | | SCEnter(); |
249 | | for (int i = 0; i < args->argc; i++) { |
250 | | if (args->argv[i] != NULL) { |
251 | | SCFree(args->argv[i]); |
252 | | args->argv[i] = NULL; |
253 | | } |
254 | | } |
255 | | |
256 | | SCFree(args->argv); |
257 | | args->argv = NULL; |
258 | | args->argc = 0; |
259 | | args->capacity = 0; |
260 | | } |
261 | | |
262 | | static void ArgumentsAdd(struct Arguments *args, char *value) |
263 | | { |
264 | | SCEnter(); |
265 | | if (args->argc + 1 > args->capacity) |
266 | | FatalError("No capacity for more arguments (Max: %" PRIu32 ")", EAL_ARGS); |
267 | | |
268 | | args->argv[args->argc++] = value; |
269 | | SCReturn; |
270 | | } |
271 | | |
272 | | static void ArgumentsAddOptionAndArgument(struct Arguments *args, const char *opt, const char *arg) |
273 | | { |
274 | | SCEnter(); |
275 | | char *option; |
276 | | char *argument; |
277 | | |
278 | | option = AllocAndSetOption(opt); |
279 | | ArgumentsAdd(args, option); |
280 | | |
281 | | // Empty argument could mean option only (e.g. --no-huge) |
282 | | if (arg == NULL || arg[0] == '\0') |
283 | | SCReturn; |
284 | | |
285 | | argument = AllocAndSetArgument(arg); |
286 | | ArgumentsAdd(args, argument); |
287 | | SCReturn; |
288 | | } |
289 | | |
290 | | static void InitEal(void) |
291 | | { |
292 | | SCEnter(); |
293 | | int retval; |
294 | | SCConfNode *param; |
295 | | const SCConfNode *eal_params = SCConfGetNode("dpdk.eal-params"); |
296 | | struct Arguments args; |
297 | | char **eal_argv; |
298 | | |
299 | | if (eal_params == NULL) { |
300 | | FatalError("DPDK EAL parameters not found in the config"); |
301 | | } |
302 | | |
303 | | ArgumentsInit(&args, EAL_ARGS); |
304 | | ArgumentsAdd(&args, AllocAndSetArgument("suricata")); |
305 | | |
306 | | TAILQ_FOREACH (param, &eal_params->head, next) { |
307 | | if (SCConfNodeIsSequence(param)) { |
308 | | const char *key = param->name; |
309 | | SCConfNode *val; |
310 | | TAILQ_FOREACH (val, ¶m->head, next) { |
311 | | ArgumentsAddOptionAndArgument(&args, key, (const char *)val->val); |
312 | | } |
313 | | continue; |
314 | | } |
315 | | ArgumentsAddOptionAndArgument(&args, param->name, param->val); |
316 | | } |
317 | | |
318 | | // creating a shallow copy for cleanup because rte_eal_init changes array contents |
319 | | eal_argv = SCCalloc(args.argc, sizeof(*args.argv)); |
320 | | if (eal_argv == NULL) { |
321 | | FatalError("Failed to allocate memory for the array of DPDK EAL arguments"); |
322 | | } |
323 | | memcpy(eal_argv, args.argv, args.argc * sizeof(*args.argv)); |
324 | | |
325 | | rte_log_set_global_level(RTE_LOG_WARNING); |
326 | | retval = rte_eal_init(args.argc, eal_argv); |
327 | | |
328 | | ArgumentsCleanup(&args); |
329 | | SCFree(eal_argv); |
330 | | |
331 | | if (retval < 0) { // retval bound to the result of rte_eal_init |
332 | | FatalError("DPDK EAL initialization error: %s", rte_strerror(-retval)); |
333 | | } |
334 | | } |
335 | | |
336 | | static void DPDKDerefConfig(void *conf) |
337 | | { |
338 | | SCEnter(); |
339 | | DPDKIfaceConfig *iconf = (DPDKIfaceConfig *)conf; |
340 | | |
341 | | if (SC_ATOMIC_SUB(iconf->ref, 1) == 1) { |
342 | | DPDKDeviceResourcesDeinit(&iconf->pkt_mempools); |
343 | | SCFree(iconf); |
344 | | } |
345 | | SCReturn; |
346 | | } |
347 | | |
348 | | static void ConfigInit(DPDKIfaceConfig **iconf) |
349 | | { |
350 | | SCEnter(); |
351 | | DPDKIfaceConfig *ptr = NULL; |
352 | | ptr = SCCalloc(1, sizeof(DPDKIfaceConfig)); |
353 | | if (ptr == NULL) |
354 | | FatalError("Could not allocate memory for DPDKIfaceConfig"); |
355 | | |
356 | | ptr->out_port_id = UINT16_MAX; // make sure no port is set |
357 | | SC_ATOMIC_INIT(ptr->ref); |
358 | | (void)SC_ATOMIC_ADD(ptr->ref, 1); |
359 | | ptr->DerefFunc = DPDKDerefConfig; |
360 | | ptr->flags = 0; |
361 | | |
362 | | *iconf = ptr; |
363 | | SCReturn; |
364 | | } |
365 | | |
366 | | static void ConfigSetIface(DPDKIfaceConfig *iconf, const char *entry_str) |
367 | | { |
368 | | SCEnter(); |
369 | | int retval; |
370 | | |
371 | | if (entry_str == NULL || entry_str[0] == '\0') |
372 | | FatalError("Interface name in DPDK config is NULL or empty"); |
373 | | |
374 | | retval = rte_eth_dev_get_port_by_name(entry_str, &iconf->port_id); |
375 | | if (retval < 0) |
376 | | FatalError("%s: interface not found: %s", entry_str, rte_strerror(-retval)); |
377 | | |
378 | | strlcpy(iconf->iface, entry_str, sizeof(iconf->iface)); |
379 | | SCReturn; |
380 | | } |
381 | | |
382 | | static int ConfigSetThreads(DPDKIfaceConfig *iconf, const char *entry_str) |
383 | | { |
384 | | SCEnter(); |
385 | | static uint16_t remaining_auto_cpus = UINT16_MAX; // uninitialized |
386 | | if (!threading_set_cpu_affinity) { |
387 | | SCLogError("DPDK runmode requires configured thread affinity"); |
388 | | SCReturnInt(-EINVAL); |
389 | | } |
390 | | |
391 | | bool wtaf_periface = true; |
392 | | ThreadsAffinityType *wtaf = GetAffinityTypeForNameAndIface("worker-cpu-set", iconf->iface); |
393 | | if (wtaf == NULL) { |
394 | | wtaf_periface = false; |
395 | | wtaf = GetAffinityTypeForNameAndIface("worker-cpu-set", NULL); // mandatory |
396 | | if (wtaf == NULL) { |
397 | | SCLogError("Specify worker-cpu-set list in the threading section"); |
398 | | SCReturnInt(-EINVAL); |
399 | | } |
400 | | } |
401 | | ThreadsAffinityType *mtaf = GetAffinityTypeForNameAndIface("management-cpu-set", NULL); |
402 | | if (mtaf == NULL) { |
403 | | SCLogError("Specify management-cpu-set list in the threading section"); |
404 | | SCReturnInt(-EINVAL); |
405 | | } |
406 | | uint16_t sched_cpus = UtilAffinityGetAffinedCPUNum(wtaf); |
407 | | if (sched_cpus == UtilCpuGetNumProcessorsOnline()) { |
408 | | SCLogWarning( |
409 | | "\"all\" specified in worker CPU cores affinity, excluding management threads"); |
410 | | UtilAffinityCpusExclude(wtaf, mtaf); |
411 | | sched_cpus = UtilAffinityGetAffinedCPUNum(wtaf); |
412 | | } |
413 | | |
414 | | if (sched_cpus == 0) { |
415 | | SCLogError("No worker CPU cores with configured affinity were configured"); |
416 | | SCReturnInt(-EINVAL); |
417 | | } else if (UtilAffinityCpusOverlap(wtaf, mtaf) != 0) { |
418 | | SCLogWarning("Worker threads should not overlap with management threads in the CPU core " |
419 | | "affinity configuration"); |
420 | | } |
421 | | |
422 | | const char *active_runmode = RunmodeGetActive(); |
423 | | if (active_runmode && !strcmp("single", active_runmode)) { |
424 | | iconf->threads = 1; |
425 | | SCReturnInt(0); |
426 | | } |
427 | | |
428 | | if (entry_str == NULL) { |
429 | | SCLogError("Number of threads for interface \"%s\" not specified", iconf->iface); |
430 | | SCReturnInt(-EINVAL); |
431 | | } |
432 | | |
433 | | if (strcmp(entry_str, "auto") == 0) { |
434 | | if (wtaf_periface) { |
435 | | iconf->threads = (uint16_t)sched_cpus; |
436 | | SCLogConfig("%s: auto-assigned %u threads", iconf->iface, iconf->threads); |
437 | | SCReturnInt(0); |
438 | | } |
439 | | |
440 | | uint16_t live_dev_count = (uint16_t)LiveGetDeviceCountWithoutAssignedThreading(); |
441 | | if (live_dev_count == 0) { |
442 | | SCLogError("No live devices found, cannot auto-assign threads"); |
443 | | SCReturnInt(-EINVAL); |
444 | | } |
445 | | iconf->threads = sched_cpus / live_dev_count; |
446 | | if (iconf->threads == 0) { |
447 | | SCLogError("Not enough worker CPU cores with affinity were configured"); |
448 | | SCReturnInt(-ERANGE); |
449 | | } |
450 | | |
451 | | if (remaining_auto_cpus == UINT16_MAX) { |
452 | | // first time auto-assignment |
453 | | remaining_auto_cpus = sched_cpus % live_dev_count; |
454 | | if (remaining_auto_cpus > 0) { |
455 | | iconf->threads++; |
456 | | remaining_auto_cpus--; |
457 | | } |
458 | | } else if (remaining_auto_cpus > 0) { |
459 | | iconf->threads++; |
460 | | remaining_auto_cpus--; |
461 | | } |
462 | | SCLogConfig("%s: auto-assigned %u threads", iconf->iface, iconf->threads); |
463 | | SCReturnInt(0); |
464 | | } |
465 | | |
466 | | if (StringParseUint16(&iconf->threads, 10, 0, entry_str) < 0) { |
467 | | SCLogError("Threads entry for interface %s contain non-numerical characters - \"%s\"", |
468 | | iconf->iface, entry_str); |
469 | | SCReturnInt(-EINVAL); |
470 | | } |
471 | | |
472 | | if (iconf->threads <= 0) { |
473 | | SCLogError("%s: positive number of threads required", iconf->iface); |
474 | | SCReturnInt(-ERANGE); |
475 | | } |
476 | | |
477 | | SCReturnInt(0); |
478 | | } |
479 | | |
480 | | static bool ConfigSetInterruptMode(DPDKIfaceConfig *iconf, bool enable) |
481 | | { |
482 | | SCEnter(); |
483 | | if (enable) |
484 | | iconf->flags |= DPDK_IRQ_MODE; |
485 | | |
486 | | SCReturnBool(true); |
487 | | } |
488 | | |
489 | | static int ConfigSetRxQueues(DPDKIfaceConfig *iconf, uint16_t nb_queues, uint16_t max_queues) |
490 | | { |
491 | | SCEnter(); |
492 | | if (nb_queues == 0) { |
493 | | SCLogInfo("%s: positive number of RX queues is required", iconf->iface); |
494 | | SCReturnInt(-ERANGE); |
495 | | } |
496 | | |
497 | | if (nb_queues > max_queues) { |
498 | | SCLogInfo("%s: number of RX queues cannot exceed %" PRIu16, iconf->iface, max_queues); |
499 | | SCReturnInt(-ERANGE); |
500 | | } |
501 | | |
502 | | iconf->nb_rx_queues = nb_queues; |
503 | | SCReturnInt(0); |
504 | | } |
505 | | |
506 | | static bool ConfigIfaceSendsPkts(const char *mode) |
507 | | { |
508 | | SCEnter(); |
509 | | if (strcmp(mode, "ips") == 0 || strcmp(mode, "tap") == 0) { |
510 | | SCReturnBool(true); |
511 | | } |
512 | | SCReturnBool(false); |
513 | | } |
514 | | |
515 | | static int ConfigSetTxQueues( |
516 | | DPDKIfaceConfig *iconf, uint16_t nb_queues, uint16_t max_queues, bool iface_sends_pkts) |
517 | | { |
518 | | SCEnter(); |
519 | | if (nb_queues == 0 && iface_sends_pkts) { |
520 | | SCLogInfo("%s: positive number of TX queues is required", iconf->iface); |
521 | | SCReturnInt(-ERANGE); |
522 | | } |
523 | | |
524 | | if (nb_queues > max_queues) { |
525 | | SCLogInfo("%s: number of TX queues cannot exceed %" PRIu16, iconf->iface, max_queues); |
526 | | SCReturnInt(-ERANGE); |
527 | | } |
528 | | |
529 | | iconf->nb_tx_queues = nb_queues; |
530 | | SCReturnInt(0); |
531 | | } |
532 | | |
533 | | static uint32_t MempoolSizeCalculate( |
534 | | const DPDKIfaceConfig *iconf, const struct rte_eth_dev_info *dev_info) |
535 | | { |
536 | | uint32_t sz = iconf->nb_rx_queues * iconf->nb_rx_desc + iconf->nb_tx_queues * iconf->nb_tx_desc; |
537 | | if (!iconf->nb_tx_queues || !iconf->nb_tx_desc) |
538 | | sz *= 2; // double to have enough space for RX descriptors |
539 | | |
540 | | if (dev_info != NULL) { |
541 | | if (strcmp(dev_info->driver_name, "net_bonding") == 0) { |
542 | | sz = BondingMempoolSizeCalculate(iconf->port_id, dev_info, sz); |
543 | | } |
544 | | } |
545 | | |
546 | | return sz; |
547 | | } |
548 | | |
549 | | static int ConfigSetMempoolSize( |
550 | | DPDKIfaceConfig *iconf, const char *entry_str, const struct rte_eth_dev_info *dev_info) |
551 | | { |
552 | | SCEnter(); |
553 | | if (entry_str == NULL || entry_str[0] == '\0' || strcmp(entry_str, "auto") == 0) { |
554 | | // calculate the mempool size based on the number of: |
555 | | // - RX / TX queues |
556 | | // - RX / TX descriptors |
557 | | bool err = false; |
558 | | if (iconf->nb_rx_queues == 0) { |
559 | | // in IDS mode, we don't need TX queues |
560 | | SCLogError("%s: cannot autocalculate mempool size without RX queues", iconf->iface); |
561 | | err = true; |
562 | | } |
563 | | |
564 | | if (iconf->nb_rx_desc == 0) { |
565 | | SCLogError( |
566 | | "%s: cannot autocalculate mempool size without RX descriptors", iconf->iface); |
567 | | err = true; |
568 | | } |
569 | | |
570 | | if (err) { |
571 | | SCReturnInt(-EINVAL); |
572 | | } |
573 | | |
574 | | iconf->mempool_size = MempoolSizeCalculate(iconf, dev_info); |
575 | | SCReturnInt(0); |
576 | | } |
577 | | |
578 | | if (StringParseUint32(&iconf->mempool_size, 10, 0, entry_str) < 0) { |
579 | | SCLogError("%s: mempool size entry contain non-numerical characters - \"%s\"", iconf->iface, |
580 | | entry_str); |
581 | | SCReturnInt(-EINVAL); |
582 | | } |
583 | | |
584 | | uint32_t required_mp_size = MempoolSizeCalculate(iconf, dev_info); |
585 | | if (required_mp_size > |
586 | | iconf->mempool_size + 1) { // +1 to mask mempool size advice given in Suricata 7.0.x - |
587 | | // mp_size should be n = (2^q - 1) |
588 | | SCLogError("%s: mempool size is likely too small for the number of descriptors and queues, " |
589 | | "set to \"auto\" or adjust to the value of \"%" PRIu32 "\"", |
590 | | iconf->iface, required_mp_size); |
591 | | SCReturnInt(-ERANGE); |
592 | | } |
593 | | |
594 | | if (iconf->mempool_size == 0) { |
595 | | SCLogError("%s: mempool size requires a positive integer", iconf->iface); |
596 | | SCReturnInt(-ERANGE); |
597 | | } |
598 | | |
599 | | SCReturnInt(0); |
600 | | } |
601 | | |
602 | | static uint32_t MempoolCacheSizeCalculate(uint32_t mp_sz) |
603 | | { |
604 | | // It is advised to have mempool cache size lower or equal to: |
605 | | // RTE_MEMPOOL_CACHE_MAX_SIZE (by default 512) and "mempool-size / 1.5" |
606 | | // and at the same time "mempool-size modulo cache_size == 0". |
607 | | uint32_t max_cache_size = MIN(RTE_MEMPOOL_CACHE_MAX_SIZE, (uint32_t)(mp_sz / 1.5)); |
608 | | return GreatestDivisorUpTo(mp_sz, max_cache_size); |
609 | | } |
610 | | |
611 | | static int ConfigSetMempoolCacheSize(DPDKIfaceConfig *iconf, const char *entry_str) |
612 | | { |
613 | | SCEnter(); |
614 | | if (entry_str == NULL || entry_str[0] == '\0' || strcmp(entry_str, "auto") == 0) { |
615 | | // calculate the mempool size based on the mempool size (it needs to be already filled in) |
616 | | if (iconf->mempool_size == 0) { |
617 | | SCLogError("%s: cannot calculate mempool cache size of a mempool with size %d", |
618 | | iconf->iface, iconf->mempool_size); |
619 | | SCReturnInt(-EINVAL); |
620 | | } |
621 | | |
622 | | iconf->mempool_cache_size_auto = true; |
623 | | SCReturnInt(0); |
624 | | } |
625 | | |
626 | | iconf->mempool_cache_size_auto = false; |
627 | | if (StringParseUint32(&iconf->mempool_cache_size, 10, 0, entry_str) < 0) { |
628 | | SCLogError("%s: mempool cache size entry contain non-numerical characters - \"%s\"", |
629 | | iconf->iface, entry_str); |
630 | | SCReturnInt(-EINVAL); |
631 | | } |
632 | | |
633 | | if (iconf->mempool_cache_size <= 0 || iconf->mempool_cache_size > RTE_MEMPOOL_CACHE_MAX_SIZE) { |
634 | | SCLogError("%s: mempool cache size requires a positive number smaller than %" PRIu32, |
635 | | iconf->iface, RTE_MEMPOOL_CACHE_MAX_SIZE); |
636 | | SCReturnInt(-ERANGE); |
637 | | } |
638 | | |
639 | | SCReturnInt(0); |
640 | | } |
641 | | |
642 | | static int ConfigSetRxDescriptors(DPDKIfaceConfig *iconf, const char *entry_str, uint16_t max_desc) |
643 | | { |
644 | | SCEnter(); |
645 | | if (entry_str == NULL || entry_str[0] == '\0') { |
646 | | SCLogInfo("%s: number of RX descriptors not found, going with: %s", iconf->iface, |
647 | | DPDK_CONFIG_DEFAULT_RX_DESCRIPTORS); |
648 | | entry_str = DPDK_CONFIG_DEFAULT_RX_DESCRIPTORS; |
649 | | } |
650 | | |
651 | | if (strcmp(entry_str, "auto") == 0) { |
652 | | iconf->nb_rx_desc = (uint16_t)GreatestPowOf2UpTo(max_desc); |
653 | | SCReturnInt(0); |
654 | | } |
655 | | |
656 | | if (StringParseUint16(&iconf->nb_rx_desc, 10, 0, entry_str) < 0) { |
657 | | SCLogError("%s: RX descriptors entry contains non-numerical characters - \"%s\"", |
658 | | iconf->iface, entry_str); |
659 | | SCReturnInt(-EINVAL); |
660 | | } |
661 | | |
662 | | if (iconf->nb_rx_desc == 0) { |
663 | | SCLogError("%s: positive number of RX descriptors is required", iconf->iface); |
664 | | SCReturnInt(-ERANGE); |
665 | | } else if (iconf->nb_rx_desc > max_desc) { |
666 | | SCLogError("%s: number of RX descriptors cannot exceed %" PRIu16, iconf->iface, max_desc); |
667 | | SCReturnInt(-ERANGE); |
668 | | } |
669 | | |
670 | | SCReturnInt(0); |
671 | | } |
672 | | |
673 | | static int ConfigSetTxDescriptors( |
674 | | DPDKIfaceConfig *iconf, const char *entry_str, uint16_t max_desc, bool iface_sends_pkts) |
675 | | { |
676 | | SCEnter(); |
677 | | if (entry_str == NULL || entry_str[0] == '\0') { |
678 | | SCLogInfo("%s: number of TX descriptors not found, going with: %s", iconf->iface, |
679 | | DPDK_CONFIG_DEFAULT_TX_DESCRIPTORS); |
680 | | entry_str = DPDK_CONFIG_DEFAULT_TX_DESCRIPTORS; |
681 | | } |
682 | | |
683 | | if (strcmp(entry_str, "auto") == 0) { |
684 | | if (iface_sends_pkts) { |
685 | | iconf->nb_tx_desc = (uint16_t)GreatestPowOf2UpTo(max_desc); |
686 | | } else { |
687 | | iconf->nb_tx_desc = 0; |
688 | | } |
689 | | SCReturnInt(0); |
690 | | } |
691 | | |
692 | | if (StringParseUint16(&iconf->nb_tx_desc, 10, 0, entry_str) < 0) { |
693 | | SCLogError("%s: TX descriptors entry contains non-numerical characters - \"%s\"", |
694 | | iconf->iface, entry_str); |
695 | | SCReturnInt(-EINVAL); |
696 | | } |
697 | | |
698 | | if (iconf->nb_tx_desc == 0 && iface_sends_pkts) { |
699 | | SCLogError("%s: positive number of TX descriptors is required", iconf->iface); |
700 | | SCReturnInt(-ERANGE); |
701 | | } else if (iconf->nb_tx_desc > max_desc) { |
702 | | SCLogError("%s: number of TX descriptors cannot exceed %" PRIu16, iconf->iface, max_desc); |
703 | | SCReturnInt(-ERANGE); |
704 | | } |
705 | | |
706 | | SCReturnInt(0); |
707 | | } |
708 | | |
709 | | static int ConfigSetRSSHashFunctions(DPDKIfaceConfig *iconf, const char *entry_str) |
710 | | { |
711 | | SCEnter(); |
712 | | if (entry_str == NULL || entry_str[0] == '\0' || strcmp(entry_str, "auto") == 0) { |
713 | | iconf->rss_hf = DPDK_CONFIG_DEFAULT_RSS_HASH_FUNCTIONS; |
714 | | SCReturnInt(0); |
715 | | } |
716 | | |
717 | | if (StringParseUint64(&iconf->rss_hf, 0, 0, entry_str) < 0) { |
718 | | SCLogError("%s: RSS hash functions entry contain non-numerical characters - \"%s\"", |
719 | | iconf->iface, entry_str); |
720 | | SCReturnInt(-EINVAL); |
721 | | } |
722 | | |
723 | | SCReturnInt(0); |
724 | | } |
725 | | |
726 | | static int ConfigSetMtu(DPDKIfaceConfig *iconf, intmax_t entry_int) |
727 | | { |
728 | | SCEnter(); |
729 | | if (entry_int < RTE_ETHER_MIN_MTU || entry_int > RTE_ETHER_MAX_JUMBO_FRAME_LEN) { |
730 | | SCLogError("%s: MTU size can only be between %" PRIu32 " and %" PRIu32, iconf->iface, |
731 | | RTE_ETHER_MIN_MTU, RTE_ETHER_MAX_JUMBO_FRAME_LEN); |
732 | | SCReturnInt(-ERANGE); |
733 | | } |
734 | | |
735 | | iconf->mtu = (uint16_t)entry_int; |
736 | | SCReturnInt(0); |
737 | | } |
738 | | |
739 | | static int ConfigSetLinkupTimeout(DPDKIfaceConfig *iconf, intmax_t entry_int) |
740 | | { |
741 | | SCEnter(); |
742 | | if (entry_int < 0 || entry_int > UINT16_MAX) { |
743 | | SCLogError("%s: Link-up waiting timeout needs to be a positive number (up to %u) or 0 to " |
744 | | "disable", |
745 | | iconf->iface, UINT16_MAX); |
746 | | SCReturnInt(-ERANGE); |
747 | | } |
748 | | |
749 | | iconf->linkup_timeout = (uint16_t)entry_int; |
750 | | SCReturnInt(0); |
751 | | } |
752 | | |
753 | | static bool ConfigSetPromiscuousMode(DPDKIfaceConfig *iconf, int entry_bool) |
754 | | { |
755 | | SCEnter(); |
756 | | if (entry_bool) |
757 | | iconf->flags |= DPDK_PROMISC; |
758 | | |
759 | | SCReturnBool(true); |
760 | | } |
761 | | |
762 | | static bool ConfigSetMulticast(DPDKIfaceConfig *iconf, int entry_bool) |
763 | | { |
764 | | SCEnter(); |
765 | | if (entry_bool) |
766 | | iconf->flags |= DPDK_MULTICAST; // enable |
767 | | |
768 | | SCReturnBool(true); |
769 | | } |
770 | | |
771 | | static int ConfigSetChecksumChecks(DPDKIfaceConfig *iconf, int entry_bool) |
772 | | { |
773 | | SCEnter(); |
774 | | if (entry_bool) |
775 | | iconf->checksum_mode = CHECKSUM_VALIDATION_ENABLE; |
776 | | |
777 | | SCReturnInt(0); |
778 | | } |
779 | | |
780 | | static int ConfigSetChecksumOffload(DPDKIfaceConfig *iconf, int entry_bool) |
781 | | { |
782 | | SCEnter(); |
783 | | if (entry_bool) |
784 | | iconf->flags |= DPDK_RX_CHECKSUM_OFFLOAD; |
785 | | |
786 | | SCReturnInt(0); |
787 | | } |
788 | | |
789 | | static void ConfigSetVlanStrip(DPDKIfaceConfig *iconf, int entry_bool) |
790 | | { |
791 | | SCEnter(); |
792 | | iconf->vlan_strip_enabled = entry_bool; |
793 | | SCReturn; |
794 | | } |
795 | | |
796 | | static int ConfigSetCopyIface(DPDKIfaceConfig *iconf, const char *entry_str) |
797 | | { |
798 | | SCEnter(); |
799 | | int retval; |
800 | | |
801 | | if (entry_str == NULL || entry_str[0] == '\0' || strcmp(entry_str, "none") == 0) { |
802 | | iconf->out_iface = NULL; |
803 | | SCReturnInt(0); |
804 | | } |
805 | | |
806 | | retval = rte_eth_dev_get_port_by_name(entry_str, &iconf->out_port_id); |
807 | | if (retval < 0) { |
808 | | SCLogError("%s: copy interface (%s) not found: %s", iconf->iface, entry_str, |
809 | | rte_strerror(-retval)); |
810 | | SCReturnInt(retval); |
811 | | } |
812 | | |
813 | | iconf->out_iface = entry_str; |
814 | | SCReturnInt(0); |
815 | | } |
816 | | |
817 | | static int ConfigSetCopyMode(DPDKIfaceConfig *iconf, const char *entry_str) |
818 | | { |
819 | | SCEnter(); |
820 | | if (entry_str == NULL) { |
821 | | SCLogWarning("%s: no copy mode specified, changing to %s ", iconf->iface, |
822 | | DPDK_CONFIG_DEFAULT_COPY_MODE); |
823 | | entry_str = DPDK_CONFIG_DEFAULT_COPY_MODE; |
824 | | } |
825 | | |
826 | | if (strcmp(entry_str, "none") != 0 && strcmp(entry_str, "tap") != 0 && |
827 | | strcmp(entry_str, "ips") != 0) { |
828 | | SCLogWarning("%s: copy mode \"%s\" is not one of the possible values (none|tap|ips). " |
829 | | "Changing to %s", |
830 | | entry_str, iconf->iface, DPDK_CONFIG_DEFAULT_COPY_MODE); |
831 | | entry_str = DPDK_CONFIG_DEFAULT_COPY_MODE; |
832 | | } |
833 | | |
834 | | if (strcmp(entry_str, "none") == 0) { |
835 | | iconf->copy_mode = DPDK_COPY_MODE_NONE; |
836 | | } else if (strcmp(entry_str, "tap") == 0) { |
837 | | iconf->copy_mode = DPDK_COPY_MODE_TAP; |
838 | | } else if (strcmp(entry_str, "ips") == 0) { |
839 | | iconf->copy_mode = DPDK_COPY_MODE_IPS; |
840 | | } |
841 | | |
842 | | SCReturnInt(0); |
843 | | } |
844 | | |
845 | | static int ConfigSetCopyIfaceSettings(DPDKIfaceConfig *iconf, const char *iface, const char *mode) |
846 | | { |
847 | | SCEnter(); |
848 | | int retval; |
849 | | |
850 | | retval = ConfigSetCopyIface(iconf, iface); |
851 | | if (retval < 0) |
852 | | SCReturnInt(retval); |
853 | | |
854 | | retval = ConfigSetCopyMode(iconf, mode); |
855 | | if (retval < 0) |
856 | | SCReturnInt(retval); |
857 | | |
858 | | if (iconf->copy_mode == DPDK_COPY_MODE_NONE) { |
859 | | if (iconf->out_iface != NULL) |
860 | | iconf->out_iface = NULL; |
861 | | SCReturnInt(0); |
862 | | } |
863 | | |
864 | | if (iconf->out_iface == NULL || strlen(iconf->out_iface) <= 0) { |
865 | | SCLogError("%s: copy mode enabled but interface not set", iconf->iface); |
866 | | SCReturnInt(-EINVAL); |
867 | | } |
868 | | |
869 | | SCReturnInt(0); |
870 | | } |
871 | | |
872 | | static int ConfigLoad(DPDKIfaceConfig *iconf, const char *iface) |
873 | | { |
874 | | SCEnter(); |
875 | | int retval; |
876 | | SCConfNode *if_root; |
877 | | SCConfNode *if_default; |
878 | | const char *entry_str = NULL; |
879 | | intmax_t entry_int = 0; |
880 | | int entry_bool = 0; |
881 | | const char *copy_iface_str = NULL; |
882 | | const char *copy_mode_str = NULL; |
883 | | |
884 | | ConfigSetIface(iconf, iface); |
885 | | struct rte_eth_dev_info dev_info = { 0 }; |
886 | | retval = rte_eth_dev_info_get(iconf->port_id, &dev_info); |
887 | | if (retval < 0) { |
888 | | SCLogError("%s: getting device info failed: %s", iconf->iface, rte_strerror(-retval)); |
889 | | SCReturnInt(retval); |
890 | | } |
891 | | |
892 | | retval = SCConfSetRootAndDefaultNodes("dpdk.interfaces", iconf->iface, &if_root, &if_default); |
893 | | if (retval < 0) { |
894 | | FatalError("failed to find DPDK configuration for the interface %s", iconf->iface); |
895 | | } |
896 | | |
897 | | retval = SCConfGetChildValueWithDefault(if_root, if_default, dpdk_yaml.threads, &entry_str) != 1 |
898 | | ? ConfigSetThreads(iconf, DPDK_CONFIG_DEFAULT_THREADS) |
899 | | : ConfigSetThreads(iconf, entry_str); |
900 | | if (retval < 0) |
901 | | SCReturnInt(retval); |
902 | | |
903 | | bool irq_enable; |
904 | | retval = SCConfGetChildValueBoolWithDefault( |
905 | | if_root, if_default, dpdk_yaml.irq_mode, &entry_bool); |
906 | | if (retval != 1) { |
907 | | irq_enable = DPDK_CONFIG_DEFAULT_INTERRUPT_MODE; |
908 | | } else { |
909 | | irq_enable = entry_bool ? true : false; |
910 | | } |
911 | | retval = ConfigSetInterruptMode(iconf, irq_enable); |
912 | | if (retval != true) |
913 | | SCReturnInt(-EINVAL); |
914 | | |
915 | | retval = SCConfGetChildValueWithDefault( |
916 | | if_root, if_default, dpdk_yaml.copy_mode, ©_mode_str); |
917 | | if (retval != 1) { |
918 | | copy_mode_str = DPDK_CONFIG_DEFAULT_COPY_MODE; |
919 | | } |
920 | | |
921 | | retval = SCConfGetChildValueWithDefault( |
922 | | if_root, if_default, dpdk_yaml.rx_descriptors, &entry_str) != 1 |
923 | | ? ConfigSetRxDescriptors(iconf, DPDK_CONFIG_DEFAULT_RX_DESCRIPTORS, |
924 | | dev_info.rx_desc_lim.nb_max) |
925 | | : ConfigSetRxDescriptors(iconf, entry_str, dev_info.rx_desc_lim.nb_max); |
926 | | if (retval < 0) |
927 | | SCReturnInt(retval); |
928 | | |
929 | | bool iface_sends_pkts = ConfigIfaceSendsPkts(copy_mode_str); |
930 | | retval = SCConfGetChildValueWithDefault( |
931 | | if_root, if_default, dpdk_yaml.tx_descriptors, &entry_str) != 1 |
932 | | ? ConfigSetTxDescriptors(iconf, DPDK_CONFIG_DEFAULT_TX_DESCRIPTORS, |
933 | | dev_info.tx_desc_lim.nb_max, iface_sends_pkts) |
934 | | : ConfigSetTxDescriptors( |
935 | | iconf, entry_str, dev_info.tx_desc_lim.nb_max, iface_sends_pkts); |
936 | | if (retval < 0) |
937 | | SCReturnInt(retval); |
938 | | |
939 | | // currently only mapping "1 thread == 1 RX (and 1 TX queue in IPS mode)" is supported |
940 | | retval = ConfigSetRxQueues(iconf, iconf->threads, dev_info.max_rx_queues); |
941 | | if (retval < 0) { |
942 | | SCLogError("%s: too many threads configured - reduce thread count to: %" PRIu16, |
943 | | iconf->iface, dev_info.max_rx_queues); |
944 | | SCReturnInt(retval); |
945 | | } |
946 | | |
947 | | // currently only mapping "1 thread == 1 RX (and 1 TX queue in IPS mode)" is supported |
948 | | uint16_t tx_queues = iconf->nb_tx_desc > 0 ? iconf->threads : 0; |
949 | | retval = ConfigSetTxQueues(iconf, tx_queues, dev_info.max_tx_queues, iface_sends_pkts); |
950 | | if (retval < 0) { |
951 | | SCLogError("%s: too many threads configured - reduce thread count to: %" PRIu16, |
952 | | iconf->iface, dev_info.max_tx_queues); |
953 | | SCReturnInt(retval); |
954 | | } |
955 | | |
956 | | retval = SCConfGetChildValueWithDefault( |
957 | | if_root, if_default, dpdk_yaml.mempool_size, &entry_str) != 1 |
958 | | ? ConfigSetMempoolSize(iconf, DPDK_CONFIG_DEFAULT_MEMPOOL_SIZE, &dev_info) |
959 | | : ConfigSetMempoolSize(iconf, entry_str, &dev_info); |
960 | | if (retval < 0) |
961 | | SCReturnInt(retval); |
962 | | |
963 | | retval = SCConfGetChildValueWithDefault( |
964 | | if_root, if_default, dpdk_yaml.mempool_cache_size, &entry_str) != 1 |
965 | | ? ConfigSetMempoolCacheSize(iconf, DPDK_CONFIG_DEFAULT_MEMPOOL_CACHE_SIZE) |
966 | | : ConfigSetMempoolCacheSize(iconf, entry_str); |
967 | | if (retval < 0) |
968 | | SCReturnInt(retval); |
969 | | |
970 | | retval = SCConfGetChildValueIntWithDefault(if_root, if_default, dpdk_yaml.mtu, &entry_int) != 1 |
971 | | ? ConfigSetMtu(iconf, DPDK_CONFIG_DEFAULT_MTU) |
972 | | : ConfigSetMtu(iconf, entry_int); |
973 | | if (retval < 0) |
974 | | SCReturnInt(retval); |
975 | | |
976 | | retval = SCConfGetChildValueWithDefault(if_root, if_default, dpdk_yaml.rss_hf, &entry_str) != 1 |
977 | | ? ConfigSetRSSHashFunctions(iconf, NULL) |
978 | | : ConfigSetRSSHashFunctions(iconf, entry_str); |
979 | | if (retval < 0) |
980 | | SCReturnInt(retval); |
981 | | |
982 | | retval = SCConfGetChildValueBoolWithDefault( |
983 | | if_root, if_default, dpdk_yaml.promisc, &entry_bool) != 1 |
984 | | ? ConfigSetPromiscuousMode(iconf, DPDK_CONFIG_DEFAULT_PROMISCUOUS_MODE) |
985 | | : ConfigSetPromiscuousMode(iconf, entry_bool); |
986 | | if (retval != true) |
987 | | SCReturnInt(-EINVAL); |
988 | | |
989 | | retval = SCConfGetChildValueBoolWithDefault( |
990 | | if_root, if_default, dpdk_yaml.multicast, &entry_bool) != 1 |
991 | | ? ConfigSetMulticast(iconf, DPDK_CONFIG_DEFAULT_MULTICAST_MODE) |
992 | | : ConfigSetMulticast(iconf, entry_bool); |
993 | | if (retval != true) |
994 | | SCReturnInt(-EINVAL); |
995 | | |
996 | | retval = SCConfGetChildValueBoolWithDefault( |
997 | | if_root, if_default, dpdk_yaml.checksum_checks, &entry_bool) != 1 |
998 | | ? ConfigSetChecksumChecks(iconf, DPDK_CONFIG_DEFAULT_CHECKSUM_VALIDATION) |
999 | | : ConfigSetChecksumChecks(iconf, entry_bool); |
1000 | | if (retval < 0) |
1001 | | SCReturnInt(retval); |
1002 | | |
1003 | | retval = SCConfGetChildValueBoolWithDefault( |
1004 | | if_root, if_default, dpdk_yaml.checksum_checks_offload, &entry_bool) != 1 |
1005 | | ? ConfigSetChecksumOffload( |
1006 | | iconf, DPDK_CONFIG_DEFAULT_CHECKSUM_VALIDATION_OFFLOAD) |
1007 | | : ConfigSetChecksumOffload(iconf, entry_bool); |
1008 | | if (retval < 0) |
1009 | | SCReturnInt(retval); |
1010 | | |
1011 | | retval = SCConfGetChildValueBoolWithDefault( |
1012 | | if_root, if_default, dpdk_yaml.vlan_strip_offload, &entry_bool); |
1013 | | if (retval != 1) { |
1014 | | ConfigSetVlanStrip(iconf, DPDK_CONFIG_DEFAULT_VLAN_STRIP); |
1015 | | } else { |
1016 | | ConfigSetVlanStrip(iconf, entry_bool); |
1017 | | } |
1018 | | |
1019 | | retval = SCConfGetChildValueIntWithDefault( |
1020 | | if_root, if_default, dpdk_yaml.linkup_timeout, &entry_int) != 1 |
1021 | | ? ConfigSetLinkupTimeout(iconf, DPDK_CONFIG_DEFAULT_LINKUP_TIMEOUT) |
1022 | | : ConfigSetLinkupTimeout(iconf, entry_int); |
1023 | | if (retval < 0) |
1024 | | SCReturnInt(retval); |
1025 | | |
1026 | | retval = SCConfGetChildValueWithDefault( |
1027 | | if_root, if_default, dpdk_yaml.copy_iface, ©_iface_str); |
1028 | | if (retval != 1) { |
1029 | | copy_iface_str = DPDK_CONFIG_DEFAULT_COPY_INTERFACE; |
1030 | | } |
1031 | | |
1032 | | retval = ConfigSetCopyIfaceSettings(iconf, copy_iface_str, copy_mode_str); |
1033 | | if (retval < 0) |
1034 | | SCReturnInt(retval); |
1035 | | |
1036 | | SCReturnInt(0); |
1037 | | } |
1038 | | |
1039 | | static bool ConfigThreadsGenericIsValid(uint16_t iface_threads, ThreadsAffinityType *wtaf) |
1040 | | { |
1041 | | static uint32_t total_cpus = 0; |
1042 | | total_cpus += iface_threads; |
1043 | | if (wtaf == NULL) { |
1044 | | SCLogError("Specify worker-cpu-set list in the threading section"); |
1045 | | return false; |
1046 | | } |
1047 | | if (total_cpus > UtilAffinityGetAffinedCPUNum(wtaf)) { |
1048 | | SCLogError("Interfaces requested more cores than configured in the worker-cpu-set " |
1049 | | "threading section (requested %d configured %d", |
1050 | | total_cpus, UtilAffinityGetAffinedCPUNum(wtaf)); |
1051 | | return false; |
1052 | | } |
1053 | | |
1054 | | return true; |
1055 | | } |
1056 | | |
1057 | | static bool ConfigThreadsInterfaceIsValid(uint16_t iface_threads, ThreadsAffinityType *itaf) |
1058 | | { |
1059 | | if (iface_threads > UtilAffinityGetAffinedCPUNum(itaf)) { |
1060 | | SCLogError("Interface requested more cores than configured in the interface-specific " |
1061 | | "threading section (requested %d configured %d", |
1062 | | iface_threads, UtilAffinityGetAffinedCPUNum(itaf)); |
1063 | | return false; |
1064 | | } |
1065 | | |
1066 | | return true; |
1067 | | } |
1068 | | |
1069 | | static bool ConfigIsThreadingValid(uint16_t iface_threads, const char *iface) |
1070 | | { |
1071 | | ThreadsAffinityType *itaf = GetAffinityTypeForNameAndIface("worker-cpu-set", iface); |
1072 | | ThreadsAffinityType *wtaf = GetAffinityTypeForNameAndIface("worker-cpu-set", NULL); |
1073 | | if (itaf && !ConfigThreadsInterfaceIsValid(iface_threads, itaf)) { |
1074 | | return false; |
1075 | | } else if (itaf == NULL && !ConfigThreadsGenericIsValid(iface_threads, wtaf)) { |
1076 | | return false; |
1077 | | } |
1078 | | return true; |
1079 | | } |
1080 | | |
1081 | | static DPDKIfaceConfig *ConfigParse(const char *iface) |
1082 | | { |
1083 | | SCEnter(); |
1084 | | int retval; |
1085 | | DPDKIfaceConfig *iconf = NULL; |
1086 | | if (iface == NULL) |
1087 | | FatalError("DPDK interface is NULL"); |
1088 | | |
1089 | | ConfigInit(&iconf); |
1090 | | retval = ConfigLoad(iconf, iface); |
1091 | | if (retval < 0 || !ConfigIsThreadingValid(iconf->threads, iface)) { |
1092 | | iconf->DerefFunc(iconf); |
1093 | | SCReturnPtr(NULL, "void *"); |
1094 | | } |
1095 | | |
1096 | | SCReturnPtr(iconf, "DPDKIfaceConfig *"); |
1097 | | } |
1098 | | |
1099 | | static void DeviceSetPMDSpecificRSS(struct rte_eth_rss_conf *rss_conf, const char *driver_name) |
1100 | | { |
1101 | | if (strcmp(driver_name, "net_i40e") == 0) |
1102 | | i40eDeviceSetRSSConf(rss_conf); |
1103 | | if (strcmp(driver_name, "net_ice") == 0) |
1104 | | iceDeviceSetRSSConf(rss_conf); |
1105 | | if (strcmp(driver_name, "net_ixgbe") == 0) |
1106 | | ixgbeDeviceSetRSSHashFunction(&rss_conf->rss_hf); |
1107 | | if (strcmp(driver_name, "net_e1000_igb") == 0) |
1108 | | rss_conf->rss_hf = (RTE_ETH_RSS_IPV4 | RTE_ETH_RSS_IPV6 | RTE_ETH_RSS_IPV6_EX); |
1109 | | } |
1110 | | |
1111 | | // Returns -1 if no bit is set |
1112 | | static int32_t GetFirstSetBitPosition(uint64_t bits) |
1113 | | { |
1114 | | for (int32_t i = 0; i < 64; i++) { |
1115 | | if (bits & BIT_U64(i)) |
1116 | | return i; |
1117 | | } |
1118 | | return -1; |
1119 | | } |
1120 | | |
1121 | | static void DumpRSSFlags(const uint64_t requested, const uint64_t actual) |
1122 | | { |
1123 | | SCLogConfig("REQUESTED (groups):"); |
1124 | | |
1125 | | SCLogConfig( |
1126 | | "RTE_ETH_RSS_IP %sset", ((requested & RTE_ETH_RSS_IP) == RTE_ETH_RSS_IP) ? "" : "NOT "); |
1127 | | SCLogConfig("RTE_ETH_RSS_TCP %sset", |
1128 | | ((requested & RTE_ETH_RSS_TCP) == RTE_ETH_RSS_TCP) ? "" : "NOT "); |
1129 | | SCLogConfig("RTE_ETH_RSS_UDP %sset", |
1130 | | ((requested & RTE_ETH_RSS_UDP) == RTE_ETH_RSS_UDP) ? "" : "NOT "); |
1131 | | SCLogConfig("RTE_ETH_RSS_SCTP %sset", |
1132 | | ((requested & RTE_ETH_RSS_SCTP) == RTE_ETH_RSS_SCTP) ? "" : "NOT "); |
1133 | | SCLogConfig("RTE_ETH_RSS_TUNNEL %sset", |
1134 | | ((requested & RTE_ETH_RSS_TUNNEL) == RTE_ETH_RSS_TUNNEL) ? "" : "NOT "); |
1135 | | |
1136 | | SCLogConfig("REQUESTED (individual):"); |
1137 | | SCLogConfig("RTE_ETH_RSS_IPV4 (Bit position: %d) %sset", |
1138 | | GetFirstSetBitPosition(RTE_ETH_RSS_IPV4), (requested & RTE_ETH_RSS_IPV4) ? "" : "NOT "); |
1139 | | SCLogConfig("RTE_ETH_RSS_FRAG_IPV4 (Bit position: %d) %sset", |
1140 | | GetFirstSetBitPosition(RTE_ETH_RSS_FRAG_IPV4), |
1141 | | (requested & RTE_ETH_RSS_FRAG_IPV4) ? "" : "NOT "); |
1142 | | SCLogConfig("RTE_ETH_RSS_NONFRAG_IPV4_TCP (Bit position: %d) %sset", |
1143 | | GetFirstSetBitPosition(RTE_ETH_RSS_NONFRAG_IPV4_TCP), |
1144 | | (requested & RTE_ETH_RSS_NONFRAG_IPV4_TCP) ? "" : "NOT "); |
1145 | | SCLogConfig("RTE_ETH_RSS_NONFRAG_IPV4_UDP (Bit position: %d) %sset", |
1146 | | GetFirstSetBitPosition(RTE_ETH_RSS_NONFRAG_IPV4_UDP), |
1147 | | (requested & RTE_ETH_RSS_NONFRAG_IPV4_UDP) ? "" : "NOT "); |
1148 | | SCLogConfig("RTE_ETH_RSS_NONFRAG_IPV4_SCTP (Bit position: %d) %sset", |
1149 | | GetFirstSetBitPosition(RTE_ETH_RSS_NONFRAG_IPV4_SCTP), |
1150 | | (requested & RTE_ETH_RSS_NONFRAG_IPV4_SCTP) ? "" : "NOT "); |
1151 | | SCLogConfig("RTE_ETH_RSS_NONFRAG_IPV4_OTHER (Bit position: %d) %sset", |
1152 | | GetFirstSetBitPosition(RTE_ETH_RSS_NONFRAG_IPV4_OTHER), |
1153 | | (requested & RTE_ETH_RSS_NONFRAG_IPV4_OTHER) ? "" : "NOT "); |
1154 | | SCLogConfig("RTE_ETH_RSS_IPV6 (Bit position: %d) %sset", |
1155 | | GetFirstSetBitPosition(RTE_ETH_RSS_IPV6), (requested & RTE_ETH_RSS_IPV6) ? "" : "NOT "); |
1156 | | SCLogConfig("RTE_ETH_RSS_FRAG_IPV6 (Bit position: %d) %sset", |
1157 | | GetFirstSetBitPosition(RTE_ETH_RSS_FRAG_IPV6), |
1158 | | (requested & RTE_ETH_RSS_FRAG_IPV6) ? "" : "NOT "); |
1159 | | SCLogConfig("RTE_ETH_RSS_NONFRAG_IPV6_TCP (Bit position: %d) %sset", |
1160 | | GetFirstSetBitPosition(RTE_ETH_RSS_NONFRAG_IPV6_TCP), |
1161 | | (requested & RTE_ETH_RSS_NONFRAG_IPV6_TCP) ? "" : "NOT "); |
1162 | | SCLogConfig("RTE_ETH_RSS_NONFRAG_IPV6_UDP (Bit position: %d) %sset", |
1163 | | GetFirstSetBitPosition(RTE_ETH_RSS_NONFRAG_IPV6_UDP), |
1164 | | (requested & RTE_ETH_RSS_NONFRAG_IPV6_UDP) ? "" : "NOT "); |
1165 | | SCLogConfig("RTE_ETH_RSS_NONFRAG_IPV6_SCTP (Bit position: %d) %sset", |
1166 | | GetFirstSetBitPosition(RTE_ETH_RSS_NONFRAG_IPV6_SCTP), |
1167 | | (requested & RTE_ETH_RSS_NONFRAG_IPV6_SCTP) ? "" : "NOT "); |
1168 | | SCLogConfig("RTE_ETH_RSS_NONFRAG_IPV6_OTHER (Bit position: %d) %sset", |
1169 | | GetFirstSetBitPosition(RTE_ETH_RSS_NONFRAG_IPV6_OTHER), |
1170 | | (requested & RTE_ETH_RSS_NONFRAG_IPV6_OTHER) ? "" : "NOT "); |
1171 | | |
1172 | | SCLogConfig("RTE_ETH_RSS_L2_PAYLOAD (Bit position: %d) %sset", |
1173 | | GetFirstSetBitPosition(RTE_ETH_RSS_L2_PAYLOAD), |
1174 | | (requested & RTE_ETH_RSS_L2_PAYLOAD) ? "" : "NOT "); |
1175 | | SCLogConfig("RTE_ETH_RSS_IPV6_EX (Bit position: %d) %sset", |
1176 | | GetFirstSetBitPosition(RTE_ETH_RSS_IPV6_EX), |
1177 | | (requested & RTE_ETH_RSS_IPV6_EX) ? "" : "NOT "); |
1178 | | SCLogConfig("RTE_ETH_RSS_IPV6_TCP_EX (Bit position: %d) %sset", |
1179 | | GetFirstSetBitPosition(RTE_ETH_RSS_IPV6_TCP_EX), |
1180 | | (requested & RTE_ETH_RSS_IPV6_TCP_EX) ? "" : "NOT "); |
1181 | | SCLogConfig("RTE_ETH_RSS_IPV6_UDP_EX (Bit position: %d) %sset", |
1182 | | GetFirstSetBitPosition(RTE_ETH_RSS_IPV6_UDP_EX), |
1183 | | (requested & RTE_ETH_RSS_IPV6_UDP_EX) ? "" : "NOT "); |
1184 | | |
1185 | | SCLogConfig("RTE_ETH_RSS_PORT (Bit position: %d) %sset", |
1186 | | GetFirstSetBitPosition(RTE_ETH_RSS_PORT), (requested & RTE_ETH_RSS_PORT) ? "" : "NOT "); |
1187 | | SCLogConfig("RTE_ETH_RSS_VXLAN (Bit position: %d) %sset", |
1188 | | GetFirstSetBitPosition(RTE_ETH_RSS_VXLAN), |
1189 | | (requested & RTE_ETH_RSS_VXLAN) ? "" : "NOT "); |
1190 | | SCLogConfig("RTE_ETH_RSS_NVGRE (Bit position: %d) %sset", |
1191 | | GetFirstSetBitPosition(RTE_ETH_RSS_NVGRE), |
1192 | | (requested & RTE_ETH_RSS_NVGRE) ? "" : "NOT "); |
1193 | | SCLogConfig("RTE_ETH_RSS_GTPU (Bit position: %d) %sset", |
1194 | | GetFirstSetBitPosition(RTE_ETH_RSS_GTPU), (requested & RTE_ETH_RSS_GTPU) ? "" : "NOT "); |
1195 | | |
1196 | | SCLogConfig("RTE_ETH_RSS_L3_SRC_ONLY (Bit position: %d) %sset", |
1197 | | GetFirstSetBitPosition(RTE_ETH_RSS_L3_SRC_ONLY), |
1198 | | (requested & RTE_ETH_RSS_L3_SRC_ONLY) ? "" : "NOT "); |
1199 | | SCLogConfig("RTE_ETH_RSS_L3_DST_ONLY (Bit position: %d) %sset", |
1200 | | GetFirstSetBitPosition(RTE_ETH_RSS_L3_DST_ONLY), |
1201 | | (requested & RTE_ETH_RSS_L3_DST_ONLY) ? "" : "NOT "); |
1202 | | SCLogConfig("RTE_ETH_RSS_L4_SRC_ONLY (Bit position: %d) %sset", |
1203 | | GetFirstSetBitPosition(RTE_ETH_RSS_L4_SRC_ONLY), |
1204 | | (requested & RTE_ETH_RSS_L4_SRC_ONLY) ? "" : "NOT "); |
1205 | | SCLogConfig("RTE_ETH_RSS_L4_DST_ONLY (Bit position: %d) %sset", |
1206 | | GetFirstSetBitPosition(RTE_ETH_RSS_L4_DST_ONLY), |
1207 | | (requested & RTE_ETH_RSS_L4_DST_ONLY) ? "" : "NOT "); |
1208 | | SCLogConfig("ACTUAL (group):"); |
1209 | | SCLogConfig( |
1210 | | "RTE_ETH_RSS_IP %sset", ((actual & RTE_ETH_RSS_IP) == RTE_ETH_RSS_IP) ? "" : "NOT "); |
1211 | | SCLogConfig( |
1212 | | "RTE_ETH_RSS_TCP %sset", ((actual & RTE_ETH_RSS_TCP) == RTE_ETH_RSS_TCP) ? "" : "NOT "); |
1213 | | SCLogConfig( |
1214 | | "RTE_ETH_RSS_UDP %sset", ((actual & RTE_ETH_RSS_UDP) == RTE_ETH_RSS_UDP) ? "" : "NOT "); |
1215 | | SCLogConfig("RTE_ETH_RSS_SCTP %sset", |
1216 | | ((actual & RTE_ETH_RSS_SCTP) == RTE_ETH_RSS_SCTP) ? "" : "NOT "); |
1217 | | SCLogConfig("RTE_ETH_RSS_TUNNEL %sset", |
1218 | | ((actual & RTE_ETH_RSS_TUNNEL) == RTE_ETH_RSS_TUNNEL) ? "" : "NOT "); |
1219 | | |
1220 | | SCLogConfig("ACTUAL (individual flags):"); |
1221 | | SCLogConfig("RTE_ETH_RSS_IPV4 %sset", (actual & RTE_ETH_RSS_IPV4) ? "" : "NOT "); |
1222 | | SCLogConfig("RTE_ETH_RSS_FRAG_IPV4 %sset", (actual & RTE_ETH_RSS_FRAG_IPV4) ? "" : "NOT "); |
1223 | | SCLogConfig("RTE_ETH_RSS_NONFRAG_IPV4_TCP %sset", |
1224 | | (actual & RTE_ETH_RSS_NONFRAG_IPV4_TCP) ? "" : "NOT "); |
1225 | | SCLogConfig("RTE_ETH_RSS_NONFRAG_IPV4_UDP %sset", |
1226 | | (actual & RTE_ETH_RSS_NONFRAG_IPV4_UDP) ? "" : "NOT "); |
1227 | | SCLogConfig("RTE_ETH_RSS_NONFRAG_IPV4_SCTP %sset", |
1228 | | (actual & RTE_ETH_RSS_NONFRAG_IPV4_SCTP) ? "" : "NOT "); |
1229 | | SCLogConfig("RTE_ETH_RSS_NONFRAG_IPV4_OTHER %sset", |
1230 | | (actual & RTE_ETH_RSS_NONFRAG_IPV4_OTHER) ? "" : "NOT "); |
1231 | | SCLogConfig("RTE_ETH_RSS_IPV6 %sset", (actual & RTE_ETH_RSS_IPV6) ? "" : "NOT "); |
1232 | | SCLogConfig("RTE_ETH_RSS_FRAG_IPV6 %sset", (actual & RTE_ETH_RSS_FRAG_IPV6) ? "" : "NOT "); |
1233 | | SCLogConfig("RTE_ETH_RSS_NONFRAG_IPV6_TCP %sset", |
1234 | | (actual & RTE_ETH_RSS_NONFRAG_IPV6_TCP) ? "" : "NOT "); |
1235 | | SCLogConfig("RTE_ETH_RSS_NONFRAG_IPV6_UDP %sset", |
1236 | | (actual & RTE_ETH_RSS_NONFRAG_IPV6_UDP) ? "" : "NOT "); |
1237 | | SCLogConfig("RTE_ETH_RSS_NONFRAG_IPV6_SCTP %sset", |
1238 | | (actual & RTE_ETH_RSS_NONFRAG_IPV6_SCTP) ? "" : "NOT "); |
1239 | | SCLogConfig("RTE_ETH_RSS_NONFRAG_IPV6_OTHER %sset", |
1240 | | (actual & RTE_ETH_RSS_NONFRAG_IPV6_OTHER) ? "" : "NOT "); |
1241 | | |
1242 | | SCLogConfig("RTE_ETH_RSS_L2_PAYLOAD %sset", (actual & RTE_ETH_RSS_L2_PAYLOAD) ? "" : "NOT "); |
1243 | | SCLogConfig("RTE_ETH_RSS_IPV6_EX %sset", (actual & RTE_ETH_RSS_IPV6_EX) ? "" : "NOT "); |
1244 | | SCLogConfig("RTE_ETH_RSS_IPV6_TCP_EX %sset", (actual & RTE_ETH_RSS_IPV6_TCP_EX) ? "" : "NOT "); |
1245 | | SCLogConfig("RTE_ETH_RSS_IPV6_UDP_EX %sset", (actual & RTE_ETH_RSS_IPV6_UDP_EX) ? "" : "NOT "); |
1246 | | |
1247 | | SCLogConfig("RTE_ETH_RSS_PORT %sset", (actual & RTE_ETH_RSS_PORT) ? "" : "NOT "); |
1248 | | SCLogConfig("RTE_ETH_RSS_VXLAN %sset", (actual & RTE_ETH_RSS_VXLAN) ? "" : "NOT "); |
1249 | | SCLogConfig("RTE_ETH_RSS_NVGRE %sset", (actual & RTE_ETH_RSS_NVGRE) ? "" : "NOT "); |
1250 | | SCLogConfig("RTE_ETH_RSS_GTPU %sset", (actual & RTE_ETH_RSS_GTPU) ? "" : "NOT "); |
1251 | | |
1252 | | SCLogConfig("RTE_ETH_RSS_L3_SRC_ONLY %sset", (actual & RTE_ETH_RSS_L3_SRC_ONLY) ? "" : "NOT "); |
1253 | | SCLogConfig("RTE_ETH_RSS_L3_DST_ONLY %sset", (actual & RTE_ETH_RSS_L3_DST_ONLY) ? "" : "NOT "); |
1254 | | SCLogConfig("RTE_ETH_RSS_L4_SRC_ONLY %sset", (actual & RTE_ETH_RSS_L4_SRC_ONLY) ? "" : "NOT "); |
1255 | | SCLogConfig("RTE_ETH_RSS_L4_DST_ONLY %sset", (actual & RTE_ETH_RSS_L4_DST_ONLY) ? "" : "NOT "); |
1256 | | } |
1257 | | |
1258 | | static void DumpRXOffloadCapabilities(const uint64_t rx_offld_capa) |
1259 | | { |
1260 | | SCLogConfig("RTE_ETH_RX_OFFLOAD_VLAN_STRIP - %savailable", |
1261 | | rx_offld_capa & RTE_ETH_RX_OFFLOAD_VLAN_STRIP ? "" : "NOT "); |
1262 | | SCLogConfig("RTE_ETH_RX_OFFLOAD_IPV4_CKSUM - %savailable", |
1263 | | rx_offld_capa & RTE_ETH_RX_OFFLOAD_IPV4_CKSUM ? "" : "NOT "); |
1264 | | SCLogConfig("RTE_ETH_RX_OFFLOAD_UDP_CKSUM - %savailable", |
1265 | | rx_offld_capa & RTE_ETH_RX_OFFLOAD_UDP_CKSUM ? "" : "NOT "); |
1266 | | SCLogConfig("RTE_ETH_RX_OFFLOAD_TCP_CKSUM - %savailable", |
1267 | | rx_offld_capa & RTE_ETH_RX_OFFLOAD_TCP_CKSUM ? "" : "NOT "); |
1268 | | SCLogConfig("RTE_ETH_RX_OFFLOAD_TCP_LRO - %savailable", |
1269 | | rx_offld_capa & RTE_ETH_RX_OFFLOAD_TCP_LRO ? "" : "NOT "); |
1270 | | SCLogConfig("RTE_ETH_RX_OFFLOAD_QINQ_STRIP - %savailable", |
1271 | | rx_offld_capa & RTE_ETH_RX_OFFLOAD_QINQ_STRIP ? "" : "NOT "); |
1272 | | SCLogConfig("RTE_ETH_RX_OFFLOAD_OUTER_IPV4_CKSUM - %savailable", |
1273 | | rx_offld_capa & RTE_ETH_RX_OFFLOAD_OUTER_IPV4_CKSUM ? "" : "NOT "); |
1274 | | SCLogConfig("RTE_ETH_RX_OFFLOAD_MACSEC_STRIP - %savailable", |
1275 | | rx_offld_capa & RTE_ETH_RX_OFFLOAD_MACSEC_STRIP ? "" : "NOT "); |
1276 | | #if RTE_VERSION < RTE_VERSION_NUM(22, 11, 0, 0) |
1277 | | SCLogConfig("RTE_ETH_RX_OFFLOAD_HEADER_SPLIT - %savailable", |
1278 | | rx_offld_capa & RTE_ETH_RX_OFFLOAD_HEADER_SPLIT ? "" : "NOT "); |
1279 | | #endif |
1280 | | SCLogConfig("RTE_ETH_RX_OFFLOAD_VLAN_FILTER - %savailable", |
1281 | | rx_offld_capa & RTE_ETH_RX_OFFLOAD_VLAN_FILTER ? "" : "NOT "); |
1282 | | SCLogConfig("RTE_ETH_RX_OFFLOAD_VLAN_EXTEND - %savailable", |
1283 | | rx_offld_capa & RTE_ETH_RX_OFFLOAD_VLAN_EXTEND ? "" : "NOT "); |
1284 | | SCLogConfig("RTE_ETH_RX_OFFLOAD_SCATTER - %savailable", |
1285 | | rx_offld_capa & RTE_ETH_RX_OFFLOAD_SCATTER ? "" : "NOT "); |
1286 | | SCLogConfig("RTE_ETH_RX_OFFLOAD_TIMESTAMP - %savailable", |
1287 | | rx_offld_capa & RTE_ETH_RX_OFFLOAD_TIMESTAMP ? "" : "NOT "); |
1288 | | SCLogConfig("RTE_ETH_RX_OFFLOAD_SECURITY - %savailable", |
1289 | | rx_offld_capa & RTE_ETH_RX_OFFLOAD_SECURITY ? "" : "NOT "); |
1290 | | SCLogConfig("RTE_ETH_RX_OFFLOAD_KEEP_CRC - %savailable", |
1291 | | rx_offld_capa & RTE_ETH_RX_OFFLOAD_KEEP_CRC ? "" : "NOT "); |
1292 | | SCLogConfig("RTE_ETH_RX_OFFLOAD_SCTP_CKSUM - %savailable", |
1293 | | rx_offld_capa & RTE_ETH_RX_OFFLOAD_SCTP_CKSUM ? "" : "NOT "); |
1294 | | SCLogConfig("RTE_ETH_RX_OFFLOAD_OUTER_UDP_CKSUM - %savailable", |
1295 | | rx_offld_capa & RTE_ETH_RX_OFFLOAD_OUTER_UDP_CKSUM ? "" : "NOT "); |
1296 | | SCLogConfig("RTE_ETH_RX_OFFLOAD_RSS_HASH - %savailable", |
1297 | | rx_offld_capa & RTE_ETH_RX_OFFLOAD_RSS_HASH ? "" : "NOT "); |
1298 | | #if RTE_VERSION >= RTE_VERSION_NUM(20, 11, 0, 0) |
1299 | | SCLogConfig("RTE_ETH_RX_OFFLOAD_BUFFER_SPLIT - %savailable", |
1300 | | rx_offld_capa & RTE_ETH_RX_OFFLOAD_BUFFER_SPLIT ? "" : "NOT "); |
1301 | | #endif |
1302 | | } |
1303 | | |
1304 | | static int DeviceValidateMTU(const DPDKIfaceConfig *iconf, const struct rte_eth_dev_info *dev_info) |
1305 | | { |
1306 | | SCEnter(); |
1307 | | if (iconf->mtu > dev_info->max_mtu || iconf->mtu < dev_info->min_mtu) { |
1308 | | SCLogError("%s: MTU out of bounds. " |
1309 | | "Min MTU: %" PRIu16 " Max MTU: %" PRIu16, |
1310 | | iconf->iface, dev_info->min_mtu, dev_info->max_mtu); |
1311 | | SCReturnInt(-ERANGE); |
1312 | | } |
1313 | | |
1314 | | #if RTE_VERSION < RTE_VERSION_NUM(21, 11, 0, 0) |
1315 | | // check if jumbo frames are set and are available |
1316 | | if (iconf->mtu > RTE_ETHER_MAX_LEN && |
1317 | | !(dev_info->rx_offload_capa & DEV_RX_OFFLOAD_JUMBO_FRAME)) { |
1318 | | SCLogError("%s: jumbo frames not supported, set MTU to 1500", iconf->iface); |
1319 | | SCReturnInt(-EINVAL); |
1320 | | } |
1321 | | #endif |
1322 | | |
1323 | | SCReturnInt(0); |
1324 | | } |
1325 | | |
1326 | | static void DeviceSetMTU(struct rte_eth_conf *port_conf, uint16_t mtu) |
1327 | | { |
1328 | | #if RTE_VERSION >= RTE_VERSION_NUM(21, 11, 0, 0) |
1329 | | port_conf->rxmode.mtu = mtu; |
1330 | | #else |
1331 | | port_conf->rxmode.max_rx_pkt_len = mtu; |
1332 | | if (mtu > RTE_ETHER_MAX_LEN) { |
1333 | | port_conf->rxmode.offloads |= DEV_RX_OFFLOAD_JUMBO_FRAME; |
1334 | | } |
1335 | | #endif |
1336 | | } |
1337 | | |
1338 | | static void PortConfSetInterruptMode(const DPDKIfaceConfig *iconf, struct rte_eth_conf *port_conf) |
1339 | | { |
1340 | | SCLogConfig("%s: interrupt mode is %s", iconf->iface, |
1341 | | iconf->flags & DPDK_IRQ_MODE ? "enabled" : "disabled"); |
1342 | | if (iconf->flags & DPDK_IRQ_MODE) |
1343 | | port_conf->intr_conf.rxq = 1; |
1344 | | } |
1345 | | |
1346 | | static void PortConfSetRSSConf(const DPDKIfaceConfig *iconf, |
1347 | | const struct rte_eth_dev_info *dev_info, struct rte_eth_conf *port_conf) |
1348 | | { |
1349 | | if (dev_info->rx_offload_capa & RTE_ETH_RX_OFFLOAD_RSS_HASH) { |
1350 | | if (iconf->nb_rx_queues > 1) { |
1351 | | SCLogConfig("%s: RSS enabled for %d queues", iconf->iface, iconf->nb_rx_queues); |
1352 | | port_conf->rx_adv_conf.rss_conf = (struct rte_eth_rss_conf){ |
1353 | | .rss_key = RSS_HKEY, |
1354 | | .rss_key_len = RSS_HKEY_LEN, |
1355 | | .rss_hf = iconf->rss_hf, |
1356 | | }; |
1357 | | |
1358 | | const char *dev_driver = dev_info->driver_name; |
1359 | | if (strcmp(dev_info->driver_name, "net_bonding") == 0) { |
1360 | | dev_driver = BondingDeviceDriverGet(iconf->port_id); |
1361 | | } |
1362 | | |
1363 | | DeviceSetPMDSpecificRSS(&port_conf->rx_adv_conf.rss_conf, dev_driver); |
1364 | | |
1365 | | uint64_t rss_hf_tmp = |
1366 | | port_conf->rx_adv_conf.rss_conf.rss_hf & dev_info->flow_type_rss_offloads; |
1367 | | if (port_conf->rx_adv_conf.rss_conf.rss_hf != rss_hf_tmp) { |
1368 | | DumpRSSFlags(port_conf->rx_adv_conf.rss_conf.rss_hf, rss_hf_tmp); |
1369 | | |
1370 | | SCLogWarning("%s: modified RSS hash function based on hardware support: " |
1371 | | "requested:%#" PRIx64 ", configured:%#" PRIx64, |
1372 | | iconf->iface, port_conf->rx_adv_conf.rss_conf.rss_hf, rss_hf_tmp); |
1373 | | port_conf->rx_adv_conf.rss_conf.rss_hf = rss_hf_tmp; |
1374 | | } |
1375 | | port_conf->rxmode.mq_mode = RTE_ETH_MQ_RX_RSS; |
1376 | | } else { |
1377 | | SCLogConfig("%s: RSS not enabled", iconf->iface); |
1378 | | port_conf->rx_adv_conf.rss_conf.rss_key = NULL; |
1379 | | port_conf->rx_adv_conf.rss_conf.rss_hf = 0; |
1380 | | } |
1381 | | } else { |
1382 | | SCLogConfig("%s: RSS not supported", iconf->iface); |
1383 | | } |
1384 | | } |
1385 | | |
1386 | | static void PortConfSetChsumOffload(const DPDKIfaceConfig *iconf, |
1387 | | const struct rte_eth_dev_info *dev_info, struct rte_eth_conf *port_conf) |
1388 | | { |
1389 | | if (iconf->checksum_mode == CHECKSUM_VALIDATION_DISABLE) { |
1390 | | SCLogConfig("%s: checksum validation disabled", iconf->iface); |
1391 | | } else if ((dev_info->rx_offload_capa & RTE_ETH_RX_OFFLOAD_CHECKSUM) == |
1392 | | RTE_ETH_RX_OFFLOAD_CHECKSUM) { // multibit comparison to make sure all bits are set |
1393 | | if (iconf->checksum_mode == CHECKSUM_VALIDATION_ENABLE && |
1394 | | iconf->flags & DPDK_RX_CHECKSUM_OFFLOAD) { |
1395 | | SCLogConfig("%s: IP, TCP and UDP checksum validation offloaded", iconf->iface); |
1396 | | port_conf->rxmode.offloads |= RTE_ETH_RX_OFFLOAD_CHECKSUM; |
1397 | | } else if (iconf->checksum_mode == CHECKSUM_VALIDATION_ENABLE && |
1398 | | !(iconf->flags & DPDK_RX_CHECKSUM_OFFLOAD)) { |
1399 | | SCLogConfig("%s: checksum validation enabled (but can be offloaded)", iconf->iface); |
1400 | | } |
1401 | | } |
1402 | | } |
1403 | | |
1404 | | static void PortConfSetVlanOffload(const DPDKIfaceConfig *iconf, |
1405 | | const struct rte_eth_dev_info *dev_info, struct rte_eth_conf *port_conf) |
1406 | | { |
1407 | | if (iconf->vlan_strip_enabled) { |
1408 | | if (dev_info->rx_offload_capa & RTE_ETH_RX_OFFLOAD_VLAN_STRIP) { |
1409 | | port_conf->rxmode.offloads |= RTE_ETH_RX_OFFLOAD_VLAN_STRIP; |
1410 | | SCLogConfig("%s: hardware VLAN stripping enabled", iconf->iface); |
1411 | | } else { |
1412 | | SCLogWarning("%s: hardware VLAN stripping enabled but not supported, disabling", |
1413 | | iconf->iface); |
1414 | | } |
1415 | | } |
1416 | | } |
1417 | | |
1418 | | static void DeviceInitPortConf(const DPDKIfaceConfig *iconf, |
1419 | | const struct rte_eth_dev_info *dev_info, struct rte_eth_conf *port_conf) |
1420 | | { |
1421 | | DumpRXOffloadCapabilities(dev_info->rx_offload_capa); |
1422 | | *port_conf = (struct rte_eth_conf){ |
1423 | | .rxmode = { |
1424 | | .mq_mode = RTE_ETH_MQ_RX_NONE, |
1425 | | .offloads = 0, // turn every offload off to prevent any packet modification |
1426 | | }, |
1427 | | .txmode = { |
1428 | | .mq_mode = RTE_ETH_MQ_TX_NONE, |
1429 | | .offloads = 0, |
1430 | | }, |
1431 | | }; |
1432 | | |
1433 | | PortConfSetInterruptMode(iconf, port_conf); |
1434 | | |
1435 | | // configure RX offloads |
1436 | | PortConfSetRSSConf(iconf, dev_info, port_conf); |
1437 | | PortConfSetChsumOffload(iconf, dev_info, port_conf); |
1438 | | DeviceSetMTU(port_conf, iconf->mtu); |
1439 | | PortConfSetVlanOffload(iconf, dev_info, port_conf); |
1440 | | |
1441 | | if (dev_info->tx_offload_capa & RTE_ETH_TX_OFFLOAD_MBUF_FAST_FREE) { |
1442 | | port_conf->txmode.offloads |= RTE_ETH_TX_OFFLOAD_MBUF_FAST_FREE; |
1443 | | } |
1444 | | } |
1445 | | |
1446 | | static int DeviceConfigureQueues(DPDKIfaceConfig *iconf, const struct rte_eth_dev_info *dev_info, |
1447 | | const struct rte_eth_conf *port_conf) |
1448 | | { |
1449 | | SCEnter(); |
1450 | | int retval; |
1451 | | struct rte_eth_rxconf rxq_conf; |
1452 | | struct rte_eth_txconf txq_conf; |
1453 | | |
1454 | | retval = DPDKDeviceResourcesInit(&(iconf->pkt_mempools), iconf->nb_rx_queues); |
1455 | | if (retval < 0) { |
1456 | | goto cleanup; |
1457 | | } |
1458 | | |
1459 | | // +4 for VLAN header |
1460 | | uint16_t mtu_size = iconf->mtu + RTE_ETHER_CRC_LEN + RTE_ETHER_HDR_LEN + 4; |
1461 | | uint16_t mbuf_size = ROUNDUP(mtu_size, 1024) + RTE_PKTMBUF_HEADROOM; |
1462 | | // ceil the div so e.g. mp_size of 262144 and 262143 both lead to 65535 on 4 rx queues |
1463 | | uint32_t q_mp_sz = (iconf->mempool_size + iconf->nb_rx_queues - 1) / iconf->nb_rx_queues - 1; |
1464 | | uint32_t q_mp_cache_sz = iconf->mempool_cache_size_auto ? MempoolCacheSizeCalculate(q_mp_sz) |
1465 | | : iconf->mempool_cache_size; |
1466 | | SCLogInfo("%s: creating %u packet mempools of size %u, cache size %u, mbuf size %u", |
1467 | | iconf->iface, iconf->nb_rx_queues, q_mp_sz, q_mp_cache_sz, mbuf_size); |
1468 | | for (int i = 0; i < iconf->nb_rx_queues; i++) { |
1469 | | char mempool_name[64]; |
1470 | | snprintf(mempool_name, sizeof(mempool_name), "mp_%d_%.20s", i, iconf->iface); |
1471 | | iconf->pkt_mempools->pkt_mp[i] = rte_pktmbuf_pool_create( |
1472 | | mempool_name, q_mp_sz, q_mp_cache_sz, 0, mbuf_size, (int)iconf->socket_id); |
1473 | | if (iconf->pkt_mempools->pkt_mp[i] == NULL) { |
1474 | | retval = -rte_errno; |
1475 | | SCLogError("%s: rte_pktmbuf_pool_create failed with code %d (mempool: %s) - %s", |
1476 | | iconf->iface, rte_errno, mempool_name, rte_strerror(rte_errno)); |
1477 | | goto cleanup; |
1478 | | } |
1479 | | } |
1480 | | |
1481 | | for (uint16_t queue_id = 0; queue_id < iconf->nb_rx_queues; queue_id++) { |
1482 | | rxq_conf = dev_info->default_rxconf; |
1483 | | rxq_conf.offloads = port_conf->rxmode.offloads; |
1484 | | rxq_conf.rx_thresh.hthresh = 0; |
1485 | | rxq_conf.rx_thresh.pthresh = 0; |
1486 | | rxq_conf.rx_thresh.wthresh = 0; |
1487 | | rxq_conf.rx_free_thresh = 0; |
1488 | | rxq_conf.rx_drop_en = 0; |
1489 | | SCLogConfig("%s: setting up RX queue %d: rx_desc: %u offloads: 0x%" PRIx64 |
1490 | | " hthresh: %" PRIu8 " pthresh: %" PRIu8 " wthresh: %" PRIu8 |
1491 | | " free_thresh: %" PRIu16 " drop_en: %" PRIu8, |
1492 | | iconf->iface, queue_id, iconf->nb_rx_desc, rxq_conf.offloads, |
1493 | | rxq_conf.rx_thresh.hthresh, rxq_conf.rx_thresh.pthresh, rxq_conf.rx_thresh.wthresh, |
1494 | | rxq_conf.rx_free_thresh, rxq_conf.rx_drop_en); |
1495 | | |
1496 | | retval = rte_eth_rx_queue_setup(iconf->port_id, queue_id, iconf->nb_rx_desc, |
1497 | | (unsigned int)iconf->socket_id, &rxq_conf, iconf->pkt_mempools->pkt_mp[queue_id]); |
1498 | | if (retval < 0) { |
1499 | | SCLogError("%s: failed to setup RX queue %u: %s", iconf->iface, queue_id, |
1500 | | rte_strerror(-retval)); |
1501 | | goto cleanup; |
1502 | | } |
1503 | | } |
1504 | | |
1505 | | for (uint16_t queue_id = 0; queue_id < iconf->nb_tx_queues; queue_id++) { |
1506 | | txq_conf = dev_info->default_txconf; |
1507 | | txq_conf.offloads = port_conf->txmode.offloads; |
1508 | | SCLogConfig("%s: setting up TX queue %d: tx_desc: %" PRIu16 " tx: offloads: 0x%" PRIx64 |
1509 | | " hthresh: %" PRIu8 " pthresh: %" PRIu8 " wthresh: %" PRIu8 |
1510 | | " tx_free_thresh: %" PRIu16 " tx_rs_thresh: %" PRIu16 |
1511 | | " txq_deferred_start: %" PRIu8, |
1512 | | iconf->iface, queue_id, iconf->nb_tx_desc, txq_conf.offloads, |
1513 | | txq_conf.tx_thresh.hthresh, txq_conf.tx_thresh.pthresh, txq_conf.tx_thresh.wthresh, |
1514 | | txq_conf.tx_free_thresh, txq_conf.tx_rs_thresh, txq_conf.tx_deferred_start); |
1515 | | retval = rte_eth_tx_queue_setup(iconf->port_id, queue_id, iconf->nb_tx_desc, |
1516 | | (unsigned int)iconf->socket_id, &txq_conf); |
1517 | | if (retval < 0) { |
1518 | | SCLogError("%s: failed to setup TX queue %u: %s", iconf->iface, queue_id, |
1519 | | rte_strerror(-retval)); |
1520 | | retval = -1; // the error code explained, informing about failure |
1521 | | goto cleanup; |
1522 | | } |
1523 | | } |
1524 | | |
1525 | | SCReturnInt(0); |
1526 | | |
1527 | | cleanup: |
1528 | | DPDKDeviceResourcesDeinit(&iconf->pkt_mempools); |
1529 | | SCReturnInt(retval); |
1530 | | } |
1531 | | |
1532 | | static int DeviceValidateOutIfaceConfig(DPDKIfaceConfig *iconf) |
1533 | | { |
1534 | | SCEnter(); |
1535 | | int retval; |
1536 | | DPDKIfaceConfig *out_iconf = NULL; |
1537 | | ConfigInit(&out_iconf); |
1538 | | if (out_iconf == NULL) { |
1539 | | FatalError("Copy interface of the interface \"%s\" is NULL", iconf->iface); |
1540 | | } |
1541 | | |
1542 | | retval = ConfigLoad(out_iconf, iconf->out_iface); |
1543 | | if (retval < 0) { |
1544 | | SCLogError("%s: fail to load config of interface", iconf->out_iface); |
1545 | | out_iconf->DerefFunc(out_iconf); |
1546 | | SCReturnInt(-EINVAL); |
1547 | | } |
1548 | | |
1549 | | if (iconf->nb_rx_queues != out_iconf->nb_tx_queues) { |
1550 | | // the other direction is validated when the copy interface is configured |
1551 | | SCLogError("%s: configured %d RX queues but copy interface %s has %d TX queues" |
1552 | | " - number of queues must be equal", |
1553 | | iconf->iface, iconf->nb_rx_queues, out_iconf->iface, out_iconf->nb_tx_queues); |
1554 | | out_iconf->DerefFunc(out_iconf); |
1555 | | SCReturnInt(-EINVAL); |
1556 | | } else if (iconf->mtu != out_iconf->mtu) { |
1557 | | SCLogError("%s: configured MTU of %d but copy interface %s has MTU set to %d" |
1558 | | " - MTU must be equal", |
1559 | | iconf->iface, iconf->mtu, out_iconf->iface, out_iconf->mtu); |
1560 | | out_iconf->DerefFunc(out_iconf); |
1561 | | SCReturnInt(-EINVAL); |
1562 | | } else if (iconf->copy_mode != out_iconf->copy_mode) { |
1563 | | SCLogError("%s: copy modes of interfaces %s and %s are not equal", iconf->iface, |
1564 | | iconf->iface, out_iconf->iface); |
1565 | | out_iconf->DerefFunc(out_iconf); |
1566 | | SCReturnInt(-EINVAL); |
1567 | | } else if (strcmp(iconf->iface, out_iconf->out_iface) != 0) { |
1568 | | // check if the other iface has the current iface set as a copy iface |
1569 | | SCLogError("%s: copy interface of %s is not set to %s", iconf->iface, out_iconf->iface, |
1570 | | iconf->iface); |
1571 | | out_iconf->DerefFunc(out_iconf); |
1572 | | SCReturnInt(-EINVAL); |
1573 | | } |
1574 | | |
1575 | | out_iconf->DerefFunc(out_iconf); |
1576 | | SCReturnInt(0); |
1577 | | } |
1578 | | |
1579 | | static int DeviceConfigureIPS(DPDKIfaceConfig *iconf) |
1580 | | { |
1581 | | SCEnter(); |
1582 | | if (iconf->out_iface != NULL) { |
1583 | | if (!rte_eth_dev_is_valid_port(iconf->out_port_id)) { |
1584 | | SCLogError("%s: retrieved copy interface port ID \"%d\" is invalid or the device is " |
1585 | | "not attached ", |
1586 | | iconf->iface, iconf->out_port_id); |
1587 | | SCReturnInt(-ENODEV); |
1588 | | } |
1589 | | int32_t out_port_socket_id; |
1590 | | int retval = DPDKDeviceSetSocketID(iconf->out_port_id, &out_port_socket_id); |
1591 | | if (retval < 0) { |
1592 | | SCLogError("%s: invalid socket id: %s", iconf->out_iface, rte_strerror(-retval)); |
1593 | | SCReturnInt(retval); |
1594 | | } |
1595 | | |
1596 | | if (iconf->socket_id != out_port_socket_id) { |
1597 | | SCLogWarning( |
1598 | | "%s: out iface %s is not on the same NUMA node (%s - NUMA %d, %s - NUMA %d)", |
1599 | | iconf->iface, iconf->out_iface, iconf->iface, iconf->socket_id, |
1600 | | iconf->out_iface, out_port_socket_id); |
1601 | | } |
1602 | | |
1603 | | retval = DeviceValidateOutIfaceConfig(iconf); |
1604 | | if (retval != 0) { |
1605 | | // Error will be written out by the validation function |
1606 | | SCReturnInt(retval); |
1607 | | } |
1608 | | |
1609 | | if (iconf->copy_mode == DPDK_COPY_MODE_IPS) |
1610 | | SCLogInfo("%s: DPDK IPS mode activated: %s->%s", iconf->iface, iconf->iface, |
1611 | | iconf->out_iface); |
1612 | | else if (iconf->copy_mode == DPDK_COPY_MODE_TAP) |
1613 | | SCLogInfo("%s: DPDK TAP mode activated: %s->%s", iconf->iface, iconf->iface, |
1614 | | iconf->out_iface); |
1615 | | } |
1616 | | SCReturnInt(0); |
1617 | | } |
1618 | | |
1619 | | /** |
1620 | | * Function verifies changes in e.g. device info after configuration has |
1621 | | * happened. Sometimes (e.g. DPDK Bond PMD with Intel NICs i40e/ixgbe) change |
1622 | | * device info only after the device configuration. |
1623 | | * @param iconf |
1624 | | * @param dev_info |
1625 | | * @return 0 on success, -EAGAIN when reconfiguration is needed, <0 on failure |
1626 | | */ |
1627 | | static int32_t DeviceVerifyPostConfigure( |
1628 | | const DPDKIfaceConfig *iconf, const struct rte_eth_dev_info *dev_info) |
1629 | | { |
1630 | | SCEnter(); |
1631 | | struct rte_eth_dev_info post_conf_dev_info = { 0 }; |
1632 | | int32_t ret = rte_eth_dev_info_get(iconf->port_id, &post_conf_dev_info); |
1633 | | if (ret < 0) { |
1634 | | SCLogError("%s: getting device info failed: %s", iconf->iface, rte_strerror(-ret)); |
1635 | | SCReturnInt(ret); |
1636 | | } |
1637 | | |
1638 | | if (dev_info->flow_type_rss_offloads != post_conf_dev_info.flow_type_rss_offloads || |
1639 | | dev_info->rx_offload_capa != post_conf_dev_info.rx_offload_capa || |
1640 | | dev_info->tx_offload_capa != post_conf_dev_info.tx_offload_capa || |
1641 | | dev_info->max_rx_queues != post_conf_dev_info.max_rx_queues || |
1642 | | dev_info->max_tx_queues != post_conf_dev_info.max_tx_queues || |
1643 | | dev_info->max_mtu != post_conf_dev_info.max_mtu) { |
1644 | | SCLogWarning("%s: device information severely changed after configuration, reconfiguring", |
1645 | | iconf->iface); |
1646 | | return -EAGAIN; |
1647 | | } |
1648 | | |
1649 | | if (strcmp(dev_info->driver_name, "net_bonding") == 0) { |
1650 | | ret = BondingAllDevicesSameDriver(iconf->port_id); |
1651 | | if (ret < 0) { |
1652 | | SCLogError("%s: bond port uses port with different DPDK drivers", iconf->iface); |
1653 | | SCReturnInt(ret); |
1654 | | } |
1655 | | } |
1656 | | |
1657 | | return 0; |
1658 | | } |
1659 | | |
1660 | | static int DeviceConfigure(DPDKIfaceConfig *iconf) |
1661 | | { |
1662 | | SCEnter(); |
1663 | | if (!rte_eth_dev_is_valid_port(iconf->port_id)) { |
1664 | | SCLogError("%s: retrieved port ID \"%d\" is invalid or the device is not attached ", |
1665 | | iconf->iface, iconf->port_id); |
1666 | | SCReturnInt(-ENODEV); |
1667 | | } |
1668 | | |
1669 | | int32_t retval = DPDKDeviceSetSocketID(iconf->port_id, &iconf->socket_id); |
1670 | | if (retval < 0) { |
1671 | | SCLogError("%s: invalid socket id: %s", iconf->iface, rte_strerror(-retval)); |
1672 | | SCReturnInt(retval); |
1673 | | } |
1674 | | |
1675 | | struct rte_eth_dev_info dev_info = { 0 }; |
1676 | | retval = rte_eth_dev_info_get(iconf->port_id, &dev_info); |
1677 | | if (retval < 0) { |
1678 | | SCLogError("%s: getting device info failed: %s", iconf->iface, rte_strerror(-retval)); |
1679 | | SCReturnInt(retval); |
1680 | | } |
1681 | | |
1682 | | if (iconf->nb_rx_queues > dev_info.max_rx_queues) { |
1683 | | SCLogError("%s: configured RX queues %u is higher than device maximum (%" PRIu16 ")", |
1684 | | iconf->iface, iconf->nb_rx_queues, dev_info.max_rx_queues); |
1685 | | SCReturnInt(-ERANGE); |
1686 | | } |
1687 | | |
1688 | | if (iconf->nb_tx_queues > dev_info.max_tx_queues) { |
1689 | | SCLogError("%s: configured TX queues %u is higher than device maximum (%" PRIu16 ")", |
1690 | | iconf->iface, iconf->nb_tx_queues, dev_info.max_tx_queues); |
1691 | | SCReturnInt(-ERANGE); |
1692 | | } |
1693 | | |
1694 | | retval = DeviceValidateMTU(iconf, &dev_info); |
1695 | | if (retval < 0) |
1696 | | return retval; |
1697 | | |
1698 | | struct rte_eth_conf port_conf = { 0 }; |
1699 | | DeviceInitPortConf(iconf, &dev_info, &port_conf); |
1700 | | if (port_conf.rxmode.offloads & RTE_ETH_RX_OFFLOAD_CHECKSUM) { |
1701 | | // Suricata does not need recalc checksums now |
1702 | | iconf->checksum_mode = CHECKSUM_VALIDATION_OFFLOAD; |
1703 | | } |
1704 | | |
1705 | | retval = rte_eth_dev_configure( |
1706 | | iconf->port_id, iconf->nb_rx_queues, iconf->nb_tx_queues, &port_conf); |
1707 | | if (retval < 0) { |
1708 | | SCLogError("%s: failed to configure the device: %s", iconf->iface, rte_strerror(-retval)); |
1709 | | SCReturnInt(retval); |
1710 | | } |
1711 | | |
1712 | | retval = DeviceVerifyPostConfigure(iconf, &dev_info); |
1713 | | if (retval < 0) |
1714 | | return retval; |
1715 | | |
1716 | | uint16_t tmp_nb_rx_desc = iconf->nb_rx_desc; |
1717 | | uint16_t tmp_nb_tx_desc = iconf->nb_tx_desc; |
1718 | | retval = rte_eth_dev_adjust_nb_rx_tx_desc( |
1719 | | iconf->port_id, &iconf->nb_rx_desc, &iconf->nb_tx_desc); |
1720 | | if (retval != 0) { |
1721 | | SCLogError("%s: failed to adjust device queue descriptors: %s", iconf->iface, |
1722 | | rte_strerror(-retval)); |
1723 | | SCReturnInt(retval); |
1724 | | } else if (tmp_nb_rx_desc != iconf->nb_rx_desc || tmp_nb_tx_desc != iconf->nb_tx_desc) { |
1725 | | SCLogWarning("%s: device queue descriptors adjusted (RX: from %u to %u, TX: from %u to %u)", |
1726 | | iconf->iface, tmp_nb_rx_desc, iconf->nb_rx_desc, tmp_nb_tx_desc, iconf->nb_tx_desc); |
1727 | | } |
1728 | | |
1729 | | retval = iconf->flags & DPDK_MULTICAST ? rte_eth_allmulticast_enable(iconf->port_id) |
1730 | | : rte_eth_allmulticast_disable(iconf->port_id); |
1731 | | if (retval == -ENOTSUP) { |
1732 | | retval = rte_eth_allmulticast_get(iconf->port_id); |
1733 | | // when multicast is enabled but set to disable or vice versa |
1734 | | if ((retval == 1 && !(iconf->flags & DPDK_MULTICAST)) || |
1735 | | (retval == 0 && (iconf->flags & DPDK_MULTICAST))) { |
1736 | | SCLogWarning("%s: cannot configure allmulticast, the port is %sin allmulticast mode", |
1737 | | iconf->iface, retval == 1 ? "" : "not "); |
1738 | | } else if (retval < 0) { |
1739 | | SCLogError("%s: failed to get multicast mode: %s", iconf->iface, rte_strerror(-retval)); |
1740 | | SCReturnInt(retval); |
1741 | | } |
1742 | | } else if (retval < 0) { |
1743 | | SCLogError("%s: error when changing multicast setting: %s", iconf->iface, |
1744 | | rte_strerror(-retval)); |
1745 | | SCReturnInt(retval); |
1746 | | } |
1747 | | |
1748 | | retval = iconf->flags & DPDK_PROMISC ? rte_eth_promiscuous_enable(iconf->port_id) |
1749 | | : rte_eth_promiscuous_disable(iconf->port_id); |
1750 | | if (retval == -ENOTSUP) { |
1751 | | retval = rte_eth_promiscuous_get(iconf->port_id); |
1752 | | if ((retval == 1 && !(iconf->flags & DPDK_PROMISC)) || |
1753 | | (retval == 0 && (iconf->flags & DPDK_PROMISC))) { |
1754 | | SCLogError("%s: cannot configure promiscuous mode, the port is in %spromiscuous mode", |
1755 | | iconf->iface, retval == 1 ? "" : "non-"); |
1756 | | SCReturnInt(TM_ECODE_FAILED); |
1757 | | } else if (retval < 0) { |
1758 | | SCLogError( |
1759 | | "%s: failed to get promiscuous mode: %s", iconf->iface, rte_strerror(-retval)); |
1760 | | SCReturnInt(retval); |
1761 | | } |
1762 | | } else if (retval < 0) { |
1763 | | SCLogError("%s: error when changing promiscuous setting: %s", iconf->iface, |
1764 | | rte_strerror(-retval)); |
1765 | | SCReturnInt(TM_ECODE_FAILED); |
1766 | | } |
1767 | | |
1768 | | // set maximum transmission unit |
1769 | | SCLogConfig("%s: setting MTU to %d", iconf->iface, iconf->mtu); |
1770 | | retval = rte_eth_dev_set_mtu(iconf->port_id, iconf->mtu); |
1771 | | if (retval == -ENOTSUP) { |
1772 | | // if it is not possible to set the MTU, retrieve it |
1773 | | retval = rte_eth_dev_get_mtu(iconf->port_id, &iconf->mtu); |
1774 | | if (retval < 0) { |
1775 | | SCLogError("%s: failed to retrieve MTU: %s", iconf->iface, rte_strerror(-retval)); |
1776 | | SCReturnInt(retval); |
1777 | | } |
1778 | | SCLogWarning( |
1779 | | "%s: changing MTU is not supported, current MTU: %u", iconf->iface, iconf->mtu); |
1780 | | } else if (retval < 0) { |
1781 | | SCLogError( |
1782 | | "%s: failed to set MTU to %u: %s", iconf->iface, iconf->mtu, rte_strerror(-retval)); |
1783 | | SCReturnInt(retval); |
1784 | | } |
1785 | | |
1786 | | retval = DeviceConfigureQueues(iconf, &dev_info, &port_conf); |
1787 | | if (retval < 0) { |
1788 | | SCReturnInt(retval); |
1789 | | } |
1790 | | |
1791 | | retval = DeviceConfigureIPS(iconf); |
1792 | | if (retval < 0) { |
1793 | | SCReturnInt(retval); |
1794 | | } |
1795 | | |
1796 | | SCReturnInt(0); |
1797 | | } |
1798 | | |
1799 | | static void *ParseDpdkConfigAndConfigureDevice(const char *iface) |
1800 | | { |
1801 | | int retval; |
1802 | | DPDKIfaceConfig *iconf = ConfigParse(iface); |
1803 | | if (iconf == NULL) { |
1804 | | FatalError("DPDK configuration could not be parsed"); |
1805 | | } |
1806 | | |
1807 | | retval = DeviceConfigure(iconf); |
1808 | | if (retval == -EAGAIN) { |
1809 | | // for e.g. bonding PMD it needs to be reconfigured |
1810 | | retval = DeviceConfigure(iconf); |
1811 | | } |
1812 | | |
1813 | | if (retval < 0) { // handles both configure attempts |
1814 | | iconf->DerefFunc(iconf); |
1815 | | if (rte_eal_cleanup() != 0) |
1816 | | FatalError("EAL cleanup failed: %s", rte_strerror(-retval)); |
1817 | | |
1818 | | if (retval == -ENOMEM) { |
1819 | | FatalError("%s: memory allocation failed - consider" |
1820 | | "%s freeing up some memory.", |
1821 | | iface, |
1822 | | rte_eal_has_hugepages() != 0 ? " increasing the number of hugepages or" : ""); |
1823 | | } else { |
1824 | | FatalError("%s: failed to configure", iface); |
1825 | | } |
1826 | | } |
1827 | | |
1828 | | SC_ATOMIC_RESET(iconf->ref); |
1829 | | (void)SC_ATOMIC_ADD(iconf->ref, iconf->threads); |
1830 | | // This counter is increased by worker threads that individually pick queue IDs. |
1831 | | SC_ATOMIC_RESET(iconf->queue_id); |
1832 | | SC_ATOMIC_RESET(iconf->inconsistent_numa_cnt); |
1833 | | iconf->workers_sync = SCCalloc(1, sizeof(*iconf->workers_sync)); |
1834 | | if (iconf->workers_sync == NULL) { |
1835 | | FatalError("Failed to allocate memory for workers_sync"); |
1836 | | } |
1837 | | SC_ATOMIC_RESET(iconf->workers_sync->worker_checked_in); |
1838 | | iconf->workers_sync->worker_cnt = iconf->threads; |
1839 | | |
1840 | | // initialize LiveDev DPDK values |
1841 | | LiveDevice *ldev_instance = LiveGetDevice(iface); |
1842 | | if (ldev_instance == NULL) { |
1843 | | FatalError("Device %s is not registered as a live device", iface); |
1844 | | } |
1845 | | ldev_instance->dpdk_vars = iconf->pkt_mempools; |
1846 | | iconf->pkt_mempools = NULL; |
1847 | | return iconf; |
1848 | | } |
1849 | | |
1850 | | /** |
1851 | | * \brief extract information from config file |
1852 | | * |
1853 | | * The returned structure will be freed by the thread init function. |
1854 | | * This is thus necessary to or copy the structure before giving it |
1855 | | * to thread or to reparse the file for each thread (and thus have |
1856 | | * new structure. |
1857 | | * |
1858 | | * After configuration is loaded, DPDK also configures the device according to the settings. |
1859 | | * |
1860 | | * \return a DPDKIfaceConfig corresponding to the interface name |
1861 | | */ |
1862 | | |
1863 | | static int DPDKConfigGetThreadsCount(void *conf) |
1864 | | { |
1865 | | if (conf == NULL) |
1866 | | FatalError("Configuration file is NULL"); |
1867 | | |
1868 | | DPDKIfaceConfig *dpdk_conf = (DPDKIfaceConfig *)conf; |
1869 | | return dpdk_conf->threads; |
1870 | | } |
1871 | | |
1872 | | #endif /* HAVE_DPDK */ |
1873 | | |
1874 | | static int DPDKRunModeIsIPS(void) |
1875 | 0 | { |
1876 | | /* Find initial node */ |
1877 | 0 | const char dpdk_node_query[] = "dpdk.interfaces"; |
1878 | 0 | SCConfNode *dpdk_node = SCConfGetNode(dpdk_node_query); |
1879 | 0 | if (dpdk_node == NULL) { |
1880 | 0 | FatalError("Unable to get %s configuration node", dpdk_node_query); |
1881 | 0 | } |
1882 | | |
1883 | 0 | const char default_iface[] = "default"; |
1884 | 0 | SCConfNode *if_default = SCConfNodeLookupKeyValue(dpdk_node, "interface", default_iface); |
1885 | 0 | int nlive = LiveGetDeviceCount(); |
1886 | 0 | bool has_ips = false; |
1887 | 0 | bool has_ids = false; |
1888 | 0 | for (int ldev = 0; ldev < nlive; ldev++) { |
1889 | 0 | const char *live_dev = LiveGetDeviceName(ldev); |
1890 | 0 | if (live_dev == NULL) |
1891 | 0 | FatalError("Unable to get device id %d from LiveDevice list", ldev); |
1892 | | |
1893 | 0 | SCConfNode *if_root = ConfFindDeviceConfig(dpdk_node, live_dev); |
1894 | 0 | if (if_root == NULL) { |
1895 | 0 | if (if_default == NULL) |
1896 | 0 | FatalError("Unable to get %s or %s interface", live_dev, default_iface); |
1897 | | |
1898 | 0 | if_root = if_default; |
1899 | 0 | } |
1900 | | |
1901 | 0 | const char *copymodestr = NULL; |
1902 | 0 | const char *copyifacestr = NULL; |
1903 | 0 | if (SCConfGetChildValueWithDefault(if_root, if_default, "copy-mode", ©modestr) == 1 && |
1904 | 0 | SCConfGetChildValue(if_root, "copy-iface", ©ifacestr) == 1) { |
1905 | 0 | if (strcmp(copymodestr, "ips") == 0) { |
1906 | 0 | has_ips = true; |
1907 | 0 | } else { |
1908 | 0 | has_ids = true; |
1909 | 0 | } |
1910 | 0 | } else { |
1911 | 0 | has_ids = true; |
1912 | 0 | } |
1913 | |
|
1914 | 0 | if (has_ids && has_ips) { |
1915 | 0 | FatalError("Copy-mode of interface %s mixes with the previously set copy-modes " |
1916 | 0 | "(only IDS/TAP and IPS copy-mode combinations are allowed in DPDK", |
1917 | 0 | live_dev); |
1918 | 0 | } |
1919 | 0 | } |
1920 | | |
1921 | 0 | return has_ips; |
1922 | 0 | } |
1923 | | |
1924 | | static int DPDKRunModeEnableIPS(void) |
1925 | 0 | { |
1926 | 0 | int r = DPDKRunModeIsIPS(); |
1927 | 0 | if (r == 1) { |
1928 | 0 | SCLogInfo("Setting IPS mode"); |
1929 | 0 | EngineModeSetIPS(); |
1930 | 0 | } |
1931 | 0 | return r; |
1932 | 0 | } |
1933 | | |
1934 | | const char *RunModeDpdkGetDefaultMode(void) |
1935 | 0 | { |
1936 | 0 | return "workers"; |
1937 | 0 | } |
1938 | | |
1939 | | void RunModeDpdkRegister(void) |
1940 | 87 | { |
1941 | 87 | RunModeRegisterNewRunMode(RUNMODE_DPDK, "workers", |
1942 | 87 | "Workers DPDK mode, each thread does all" |
1943 | 87 | " tasks from acquisition to logging", |
1944 | 87 | RunModeIdsDpdkWorkers, DPDKRunModeEnableIPS); |
1945 | 87 | } |
1946 | | |
1947 | | /** |
1948 | | * \brief Workers version of the DPDK processing. |
1949 | | * |
1950 | | * Start N threads with each thread doing all the work. |
1951 | | * |
1952 | | */ |
1953 | | int RunModeIdsDpdkWorkers(void) |
1954 | 0 | { |
1955 | 0 | SCEnter(); |
1956 | | #ifdef HAVE_DPDK |
1957 | | int ret; |
1958 | | |
1959 | | TimeModeSetLive(); |
1960 | | |
1961 | | InitEal(); |
1962 | | ret = RunModeSetLiveCaptureWorkers(ParseDpdkConfigAndConfigureDevice, DPDKConfigGetThreadsCount, |
1963 | | "ReceiveDPDK", "DecodeDPDK", thread_name_workers, NULL); |
1964 | | if (ret != 0) { |
1965 | | FatalError("Unable to start runmode"); |
1966 | | } |
1967 | | |
1968 | | SCLogDebug("RunModeIdsDpdkWorkers initialised"); |
1969 | | |
1970 | | #endif /* HAVE_DPDK */ |
1971 | 0 | SCReturnInt(0); |
1972 | 0 | } |
1973 | | |
1974 | | /** |
1975 | | * @} |
1976 | | */ |