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)
50 static 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 
89 static 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 
121 static 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 
153 static 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 
176 static 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 
220 static 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 
264 static 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 
302 int i40eDeviceSetRSS(int port_id, int 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 
314 void 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  */
offset
uint64_t offset
Definition: util-streaming-buffer.h:0
util-dpdk-rss.h
util-debug.h
util-dpdk.h
FatalError
#define FatalError(...)
Definition: util-debug.h:502
SCLogError
#define SCLogError(...)
Macro used to log ERROR messages.
Definition: util-debug.h:261
util-dpdk-bonding.h
util-dpdk-i40e.h