suricata
util-dpdk-i40e.c
Go to the documentation of this file.
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 * \defgroup dpdk DPDK Intel I40E driver helpers functions
20 *
21 * @{
22 */
23
24/**
25 * \file
26 *
27 * \author Lukas Sismis <lukas.sismis@gmail.com>
28 *
29 * DPDK driver's helper functions
30 *
31 */
32
33#include "util-dpdk-i40e.h"
34#include "util-dpdk.h"
35#include "util-debug.h"
36#include "util-dpdk-bonding.h"
37#include "util-dpdk-rss.h"
38
39#ifdef HAVE_DPDK
40
41#if RTE_VERSION < RTE_VERSION_NUM(21, 0, 0, 0)
42#define I40E_RSS_HKEY_LEN 40
43#define I40E_RSS_HASH_FUNCTION RTE_ETH_HASH_FUNCTION_SYMMETRIC_TOEPLITZ
44#else
45#define I40E_RSS_HKEY_LEN 52
46#define I40E_RSS_HASH_FUNCTION RTE_ETH_HASH_FUNCTION_TOEPLITZ
47#endif // RTE_VERSION < RTE_VERSION_NUM(21, 0, 0, 0)
48
49#if RTE_VERSION < RTE_VERSION_NUM(20, 0, 0, 0)
50static int i40eDeviceEnableSymHash(
51 int port_id, const char *port_name, uint32_t ftype, enum rte_eth_hash_function function)
52{
53 struct rte_eth_hash_filter_info info;
54 int retval;
55 uint32_t idx, offset;
56
57 memset(&info, 0, sizeof(info));
58
59#pragma GCC diagnostic push
60#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
61 retval = rte_eth_dev_filter_supported(port_id, RTE_ETH_FILTER_HASH);
62#pragma GCC diagnostic pop
63 if (retval < 0) {
64 SCLogError("%s: RTE_ETH_FILTER_HASH not supported", port_name);
65 return retval;
66 }
67
68 info.info_type = RTE_ETH_HASH_FILTER_GLOBAL_CONFIG;
69 info.info.global_conf.hash_func = function;
70
71 idx = ftype / UINT64_BIT;
72 offset = ftype % UINT64_BIT;
73 info.info.global_conf.valid_bit_mask[idx] |= (1ULL << offset);
74 info.info.global_conf.sym_hash_enable_mask[idx] |= (1ULL << offset);
75
76#pragma GCC diagnostic push
77#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
78 retval = rte_eth_dev_filter_ctrl(port_id, RTE_ETH_FILTER_HASH, RTE_ETH_FILTER_SET, &info);
79#pragma GCC diagnostic pop
80
81 if (retval < 0) {
82 SCLogError("%s: cannot set global hash configurations", port_name);
83 return retval;
84 }
85
86 return 0;
87}
88
89static int i40eDeviceSetSymHash(int port_id, const char *port_name, int enable)
90{
91 int ret;
92 struct rte_eth_hash_filter_info info;
93
94 memset(&info, 0, sizeof(info));
95
96#pragma GCC diagnostic push
97#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
98 ret = rte_eth_dev_filter_supported(port_id, RTE_ETH_FILTER_HASH);
99#pragma GCC diagnostic pop
100
101 if (ret < 0) {
102 SCLogError("%s: RTE_ETH_FILTER_HASH not supported", port_name);
103 return ret;
104 }
105
106 info.info_type = RTE_ETH_HASH_FILTER_SYM_HASH_ENA_PER_PORT;
107 info.info.enable = enable;
108#pragma GCC diagnostic push
109#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
110 ret = rte_eth_dev_filter_ctrl(port_id, RTE_ETH_FILTER_HASH, RTE_ETH_FILTER_SET, &info);
111#pragma GCC diagnostic pop
112
113 if (ret < 0) {
114 SCLogError("%s: cannot set symmetric hash enable per port", port_name);
115 return ret;
116 }
117
118 return 0;
119}
120
121static int i40eDeviceApplyRSSFilter(int port_id, const char *port_name)
122{
123 int retval = 0;
124
125 // Behavior of RTE_FLOW in DPDK version 19.xx and less is different than on versions
126 // above. For that reason RSS on i40e driver is set differently.
127 retval |= i40eDeviceEnableSymHash(
128 port_id, port_name, RTE_ETH_FLOW_FRAG_IPV4, RTE_ETH_HASH_FUNCTION_TOEPLITZ);
129 retval |= i40eDeviceEnableSymHash(
130 port_id, port_name, RTE_ETH_FLOW_NONFRAG_IPV4_TCP, RTE_ETH_HASH_FUNCTION_TOEPLITZ);
131 retval |= i40eDeviceEnableSymHash(
132 port_id, port_name, RTE_ETH_FLOW_NONFRAG_IPV4_UDP, RTE_ETH_HASH_FUNCTION_TOEPLITZ);
133 retval |= i40eDeviceEnableSymHash(
134 port_id, port_name, RTE_ETH_FLOW_NONFRAG_IPV4_SCTP, RTE_ETH_HASH_FUNCTION_TOEPLITZ);
135 retval |= i40eDeviceEnableSymHash(
136 port_id, port_name, RTE_ETH_FLOW_NONFRAG_IPV4_OTHER, RTE_ETH_HASH_FUNCTION_TOEPLITZ);
137
138 retval |= i40eDeviceEnableSymHash(
139 port_id, port_name, RTE_ETH_FLOW_FRAG_IPV6, RTE_ETH_HASH_FUNCTION_TOEPLITZ);
140 retval |= i40eDeviceEnableSymHash(
141 port_id, port_name, RTE_ETH_FLOW_NONFRAG_IPV6_TCP, RTE_ETH_HASH_FUNCTION_TOEPLITZ);
142 retval |= i40eDeviceEnableSymHash(
143 port_id, port_name, RTE_ETH_FLOW_NONFRAG_IPV6_UDP, RTE_ETH_HASH_FUNCTION_TOEPLITZ);
144 retval |= i40eDeviceEnableSymHash(
145 port_id, port_name, RTE_ETH_FLOW_NONFRAG_IPV6_SCTP, RTE_ETH_HASH_FUNCTION_TOEPLITZ);
146 retval |= i40eDeviceEnableSymHash(
147 port_id, port_name, RTE_ETH_FLOW_NONFRAG_IPV6_OTHER, RTE_ETH_HASH_FUNCTION_TOEPLITZ);
148
149 retval |= i40eDeviceSetSymHash(port_id, port_name, 1);
150 return retval;
151}
152
153static int32_t i40eDeviceSetRSSWithFilter(int port_id, const char *port_name)
154{
155 int32_t ret = BondingIsBond(port_id);
156 if (ret < 0)
157 return -ret;
158
159 if (ret == 1) { // regular device
160 i40eDeviceApplyRSSFilter(port_id, port_name);
161 } else if (ret == 0) { // the device is Bond PMD
162 uint16_t bonded_devs[RTE_MAX_ETHPORTS];
163 ret = BondingMemberDevicesGet(port_id, bonded_devs, RTE_MAX_ETHPORTS);
164 for (int i = 0; i < ret; i++) {
165 i40eDeviceApplyRSSFilter(bonded_devs[i], port_name);
166 }
167 } else {
168 FatalError("Unknown return value from BondingIsBond()");
169 }
170
171 return 0;
172}
173
174#else
175
176static int i40eDeviceSetRSSFlowIPv4(
177 int port_id, const char *port_name, struct rte_flow_action_rss rss_conf)
178{
179 int ret = 0;
180 struct rte_flow_item pattern[] = { { 0 }, { 0 }, { 0 }, { 0 } };
181
182 pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH;
183 pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV4;
184 pattern[2].type = RTE_FLOW_ITEM_TYPE_END;
185 ret |= DPDKCreateRSSFlow(port_id, port_name, rss_conf,
186 RTE_ETH_RSS_NONFRAG_IPV4_OTHER | RTE_ETH_RSS_L3_SRC_ONLY | RTE_ETH_RSS_L3_DST_ONLY,
187 pattern);
188 memset(pattern, 0, sizeof(pattern));
189
190 pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH;
191 pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV4;
192 pattern[2].type = RTE_FLOW_ITEM_TYPE_UDP;
193 pattern[3].type = RTE_FLOW_ITEM_TYPE_END;
194 ret |= DPDKCreateRSSFlow(port_id, port_name, rss_conf,
195 RTE_ETH_RSS_NONFRAG_IPV4_UDP | RTE_ETH_RSS_L3_SRC_ONLY | RTE_ETH_RSS_L3_DST_ONLY,
196 pattern);
197 memset(pattern, 0, sizeof(pattern));
198
199 pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH;
200 pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV4;
201 pattern[2].type = RTE_FLOW_ITEM_TYPE_TCP;
202 pattern[3].type = RTE_FLOW_ITEM_TYPE_END;
203 ret |= DPDKCreateRSSFlow(port_id, port_name, rss_conf,
204 RTE_ETH_RSS_NONFRAG_IPV4_TCP | RTE_ETH_RSS_L3_SRC_ONLY | RTE_ETH_RSS_L3_DST_ONLY,
205 pattern);
206 memset(pattern, 0, sizeof(pattern));
207
208 pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH;
209 pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV4;
210 pattern[2].type = RTE_FLOW_ITEM_TYPE_SCTP;
211 pattern[3].type = RTE_FLOW_ITEM_TYPE_END;
212 ret |= DPDKCreateRSSFlow(port_id, port_name, rss_conf,
213 RTE_ETH_RSS_NONFRAG_IPV4_SCTP | RTE_ETH_RSS_L3_SRC_ONLY | RTE_ETH_RSS_L3_DST_ONLY,
214 pattern);
215 memset(pattern, 0, sizeof(pattern));
216
217 return ret;
218}
219
220static int i40eDeviceSetRSSFlowIPv6(
221 int port_id, const char *port_name, struct rte_flow_action_rss rss_conf)
222{
223 int ret = 0;
224 struct rte_flow_item pattern[] = { { 0 }, { 0 }, { 0 }, { 0 } };
225
226 pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH;
227 pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV6;
228 pattern[2].type = RTE_FLOW_ITEM_TYPE_END;
229 ret |= DPDKCreateRSSFlow(port_id, port_name, rss_conf,
230 RTE_ETH_RSS_NONFRAG_IPV6_OTHER | RTE_ETH_RSS_L3_SRC_ONLY | RTE_ETH_RSS_L3_DST_ONLY,
231 pattern);
232 memset(pattern, 0, sizeof(pattern));
233
234 pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH;
235 pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV6;
236 pattern[2].type = RTE_FLOW_ITEM_TYPE_UDP;
237 pattern[3].type = RTE_FLOW_ITEM_TYPE_END;
238 ret |= DPDKCreateRSSFlow(port_id, port_name, rss_conf,
239 RTE_ETH_RSS_NONFRAG_IPV6_UDP | RTE_ETH_RSS_L3_SRC_ONLY | RTE_ETH_RSS_L3_DST_ONLY,
240 pattern);
241 memset(pattern, 0, sizeof(pattern));
242
243 pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH;
244 pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV6;
245 pattern[2].type = RTE_FLOW_ITEM_TYPE_TCP;
246 pattern[3].type = RTE_FLOW_ITEM_TYPE_END;
247 ret |= DPDKCreateRSSFlow(port_id, port_name, rss_conf,
248 RTE_ETH_RSS_NONFRAG_IPV6_TCP | RTE_ETH_RSS_L3_SRC_ONLY | RTE_ETH_RSS_L3_DST_ONLY,
249 pattern);
250 memset(pattern, 0, sizeof(pattern));
251
252 pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH;
253 pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV6;
254 pattern[2].type = RTE_FLOW_ITEM_TYPE_SCTP;
255 pattern[3].type = RTE_FLOW_ITEM_TYPE_END;
256 ret |= DPDKCreateRSSFlow(port_id, port_name, rss_conf,
257 RTE_ETH_RSS_NONFRAG_IPV6_SCTP | RTE_ETH_RSS_L3_SRC_ONLY | RTE_ETH_RSS_L3_DST_ONLY,
258 pattern);
259 memset(pattern, 0, sizeof(pattern));
260
261 return ret;
262}
263
264static int i40eDeviceSetRSSWithFlows(int port_id, const char *port_name, int nb_rx_queues)
265{
266 uint16_t queues[RTE_MAX_QUEUES_PER_PORT];
267 struct rte_flow_error flush_error = { 0 };
268 struct rte_eth_rss_conf rss_conf = {
269 .rss_key = RSS_HKEY,
270 .rss_key_len = I40E_RSS_HKEY_LEN,
271 };
272
273 if (nb_rx_queues < 1) {
274 FatalError("The number of queues for RSS configuration must be "
275 "configured with a positive number");
276 }
277
278 struct rte_flow_action_rss rss_action_conf =
279 DPDKInitRSSAction(rss_conf, nb_rx_queues, queues, RTE_ETH_HASH_FUNCTION_DEFAULT, false);
280
281 int retval = DPDKSetRSSFlowQueues(port_id, port_name, rss_action_conf);
282
283 memset(&rss_action_conf, 0, sizeof(struct rte_flow_action_rss));
284 rss_action_conf = DPDKInitRSSAction(rss_conf, 0, queues, I40E_RSS_HASH_FUNCTION, true);
285
286 retval |= i40eDeviceSetRSSFlowIPv4(port_id, port_name, rss_action_conf);
287 retval |= i40eDeviceSetRSSFlowIPv6(port_id, port_name, rss_action_conf);
288 if (retval != 0) {
289 retval = rte_flow_flush(port_id, &flush_error);
290 if (retval != 0) {
291 SCLogError("%s: unable to flush rte_flow rules: %s Flush error msg: %s", port_name,
292 rte_strerror(-retval), flush_error.message);
293 }
294 return retval;
295 }
296
297 return 0;
298}
299
300#endif /* RTE_VERSION < RTE_VERSION_NUM(20,0,0,0) */
301
302int i40eDeviceSetRSS(int port_id, uint16_t nb_rx_queues, char *port_name)
303{
304 (void)nb_rx_queues; // avoid unused variable warnings
305
306#if RTE_VERSION >= RTE_VERSION_NUM(20, 0, 0, 0)
307 i40eDeviceSetRSSWithFlows(port_id, port_name, nb_rx_queues);
308#else
309 i40eDeviceSetRSSWithFilter(port_id, port_name);
310#endif
311 return 0;
312}
313
314void i40eDeviceSetRSSConf(struct rte_eth_rss_conf *rss_conf)
315{
316#if RTE_VERSION >= RTE_VERSION_NUM(20, 0, 0, 0)
317 rss_conf->rss_hf = RTE_ETH_RSS_FRAG_IPV4 | RTE_ETH_RSS_NONFRAG_IPV4_OTHER |
318 RTE_ETH_RSS_FRAG_IPV6 | RTE_ETH_RSS_NONFRAG_IPV6_OTHER;
319 rss_conf->rss_key = NULL;
320 rss_conf->rss_key_len = 0;
321#else
322 rss_conf->rss_hf =
323 RTE_ETH_RSS_FRAG_IPV4 | RTE_ETH_RSS_NONFRAG_IPV4_TCP | RTE_ETH_RSS_NONFRAG_IPV4_UDP |
324 RTE_ETH_RSS_NONFRAG_IPV4_SCTP | RTE_ETH_RSS_NONFRAG_IPV4_OTHER | RTE_ETH_RSS_FRAG_IPV6 |
325 RTE_ETH_RSS_NONFRAG_IPV6_TCP | RTE_ETH_RSS_NONFRAG_IPV6_UDP |
326 RTE_ETH_RSS_NONFRAG_IPV6_SCTP | RTE_ETH_RSS_NONFRAG_IPV6_OTHER | RTE_ETH_RSS_SCTP;
327#endif
328}
329
330#endif /* HAVE_DPDK */
331/**
332 * @}
333 */
#define FatalError(...)
Definition util-debug.h:510
#define SCLogError(...)
Macro used to log ERROR messages.
Definition util-debug.h:267
uint64_t offset