suricata
util-dpdk-i40e.c
Go to the documentation of this file.
1 /* Copyright (C) 2021 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 
37 #ifdef HAVE_DPDK
38 
39 #define I40E_RSS_HKEY_LEN 52
40 
41 #if RTE_VER_YEAR <= 19
42 static int i40eDeviceEnableSymHash(
43  int port_id, const char *port_name, uint32_t ftype, enum rte_eth_hash_function function)
44 {
45  struct rte_eth_hash_filter_info info;
46  int retval;
47  uint32_t idx, offset;
48 
49  memset(&info, 0, sizeof(info));
50 
51 #pragma GCC diagnostic push
52 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
53  retval = rte_eth_dev_filter_supported(port_id, RTE_ETH_FILTER_HASH);
54 #pragma GCC diagnostic pop
55  if (retval < 0) {
56  SCLogError("RTE_ETH_FILTER_HASH not supported on port: %s", port_name);
57  return retval;
58  }
59 
60  info.info_type = RTE_ETH_HASH_FILTER_GLOBAL_CONFIG;
61  info.info.global_conf.hash_func = function;
62 
63  idx = ftype / UINT64_BIT;
64  offset = ftype % UINT64_BIT;
65  info.info.global_conf.valid_bit_mask[idx] |= (1ULL << offset);
66  info.info.global_conf.sym_hash_enable_mask[idx] |= (1ULL << offset);
67 
68 #pragma GCC diagnostic push
69 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
70  retval = rte_eth_dev_filter_ctrl(port_id, RTE_ETH_FILTER_HASH, RTE_ETH_FILTER_SET, &info);
71 #pragma GCC diagnostic pop
72 
73  if (retval < 0) {
74  SCLogError("Cannot set global hash configurations on port %s", port_name);
75  return retval;
76  }
77 
78  return 0;
79 }
80 
81 static int i40eDeviceSetSymHash(int port_id, const char *port_name, int enable)
82 {
83  int ret;
84  struct rte_eth_hash_filter_info info;
85 
86  memset(&info, 0, sizeof(info));
87 
88 #pragma GCC diagnostic push
89 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
90  ret = rte_eth_dev_filter_supported(port_id, RTE_ETH_FILTER_HASH);
91 #pragma GCC diagnostic pop
92 
93  if (ret < 0) {
94  SCLogError("RTE_ETH_FILTER_HASH not supported on port: %s", port_name);
95  return ret;
96  }
97 
98  info.info_type = RTE_ETH_HASH_FILTER_SYM_HASH_ENA_PER_PORT;
99  info.info.enable = enable;
100 #pragma GCC diagnostic push
101 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
102  ret = rte_eth_dev_filter_ctrl(port_id, RTE_ETH_FILTER_HASH, RTE_ETH_FILTER_SET, &info);
103 #pragma GCC diagnostic pop
104 
105  if (ret < 0) {
106  SCLogError("Cannot set symmetric hash enable per port on port %s", port_name);
107  return ret;
108  }
109 
110  return 0;
111 }
112 
113 static int i40eDeviceSetRSSWithFilter(int port_id, const char *port_name)
114 {
115  int retval = 0;
116 
117  // Behavior of RTE_FLOW in DPDK version 19.xx and less is different than on versions
118  // above. For that reason RSS on i40e driver is set differently.
119  retval |= i40eDeviceEnableSymHash(
120  port_id, port_name, RTE_ETH_FLOW_FRAG_IPV4, RTE_ETH_HASH_FUNCTION_TOEPLITZ);
121  retval |= i40eDeviceEnableSymHash(
122  port_id, port_name, RTE_ETH_FLOW_NONFRAG_IPV4_TCP, RTE_ETH_HASH_FUNCTION_TOEPLITZ);
123  retval |= i40eDeviceEnableSymHash(
124  port_id, port_name, RTE_ETH_FLOW_NONFRAG_IPV4_UDP, RTE_ETH_HASH_FUNCTION_TOEPLITZ);
125  retval |= i40eDeviceEnableSymHash(
126  port_id, port_name, RTE_ETH_FLOW_NONFRAG_IPV4_SCTP, RTE_ETH_HASH_FUNCTION_TOEPLITZ);
127  retval |= i40eDeviceEnableSymHash(
128  port_id, port_name, RTE_ETH_FLOW_NONFRAG_IPV4_OTHER, RTE_ETH_HASH_FUNCTION_TOEPLITZ);
129 
130  retval |= i40eDeviceEnableSymHash(
131  port_id, port_name, RTE_ETH_FLOW_FRAG_IPV6, RTE_ETH_HASH_FUNCTION_TOEPLITZ);
132  retval |= i40eDeviceEnableSymHash(
133  port_id, port_name, RTE_ETH_FLOW_NONFRAG_IPV6_TCP, RTE_ETH_HASH_FUNCTION_TOEPLITZ);
134  retval |= i40eDeviceEnableSymHash(
135  port_id, port_name, RTE_ETH_FLOW_NONFRAG_IPV6_UDP, RTE_ETH_HASH_FUNCTION_TOEPLITZ);
136  retval |= i40eDeviceEnableSymHash(
137  port_id, port_name, RTE_ETH_FLOW_NONFRAG_IPV6_SCTP, RTE_ETH_HASH_FUNCTION_TOEPLITZ);
138  retval |= i40eDeviceEnableSymHash(
139  port_id, port_name, RTE_ETH_FLOW_NONFRAG_IPV6_OTHER, RTE_ETH_HASH_FUNCTION_TOEPLITZ);
140 
141  retval |= i40eDeviceSetSymHash(port_id, port_name, 1);
142  return retval;
143 }
144 
145 #else
146 
147 static int i40eDeviceSetRSSFlowQueues(
148  int port_id, const char *port_name, struct rte_eth_rss_conf rss_conf, int nb_rx_queues)
149 {
150  struct rte_flow_action_rss rss_action_conf = { 0 };
151  struct rte_flow_attr attr = { 0 };
152  struct rte_flow_item pattern[] = { { 0 }, { 0 }, { 0 }, { 0 } };
153  struct rte_flow_action action[] = { { 0 }, { 0 } };
154  struct rte_flow *flow;
155  struct rte_flow_error flow_error = { 0 };
156  uint16_t queues[RTE_MAX_QUEUES_PER_PORT];
157 
158  for (int i = 0; i < nb_rx_queues; ++i)
159  queues[i] = i;
160 
161  rss_action_conf.func = RTE_ETH_HASH_FUNCTION_DEFAULT;
162  rss_action_conf.level = 0;
163  rss_action_conf.types = 0; // queues region can not be configured with types
164  rss_action_conf.key_len = 0;
165  rss_action_conf.key = NULL;
166 
167  if (nb_rx_queues < 1) {
168  FatalError("The number of queues for RSS configuration must be "
169  "configured with a positive number");
170  }
171 
172  rss_action_conf.queue_num = nb_rx_queues;
173  rss_action_conf.queue = queues;
174 
175  attr.ingress = 1;
176  pattern[0].type = RTE_FLOW_ITEM_TYPE_END;
177  action[0].type = RTE_FLOW_ACTION_TYPE_RSS;
178  action[0].conf = &rss_action_conf;
179  action[1].type = RTE_FLOW_ACTION_TYPE_END;
180 
181  flow = rte_flow_create(port_id, &attr, pattern, action, &flow_error);
182  if (flow == NULL) {
183  SCLogError("Error when creating rte_flow rule on %s: %s", port_name, flow_error.message);
184  int ret = rte_flow_validate(port_id, &attr, pattern, action, &flow_error);
185  SCLogError("Error on rte_flow validation for port %s: %s errmsg: %s", port_name,
186  rte_strerror(-ret), flow_error.message);
187  return ret;
188  } else {
189  SCLogInfo("RTE_FLOW queue region created for port %s", port_name);
190  }
191  return 0;
192 }
193 
194 static int i40eDeviceCreateRSSFlow(int port_id, const char *port_name,
195  struct rte_eth_rss_conf rss_conf, uint64_t rss_type, struct rte_flow_item *pattern)
196 {
197  struct rte_flow_action_rss rss_action_conf = { 0 };
198  struct rte_flow_attr attr = { 0 };
199  struct rte_flow_action action[] = { { 0 }, { 0 } };
200  struct rte_flow *flow;
201  struct rte_flow_error flow_error = { 0 };
202 
203  rss_action_conf.func = RTE_ETH_HASH_FUNCTION_SYMMETRIC_TOEPLITZ;
204  rss_action_conf.level = 0;
205  rss_action_conf.types = rss_type;
206  rss_action_conf.key_len = rss_conf.rss_key_len;
207  rss_action_conf.key = rss_conf.rss_key;
208  rss_action_conf.queue_num = 0;
209  rss_action_conf.queue = NULL;
210 
211  attr.ingress = 1;
212  action[0].type = RTE_FLOW_ACTION_TYPE_RSS;
213  action[0].conf = &rss_action_conf;
214  action[1].type = RTE_FLOW_ACTION_TYPE_END;
215 
216  flow = rte_flow_create(port_id, &attr, pattern, action, &flow_error);
217  if (flow == NULL) {
218  SCLogError("Error when creating rte_flow rule on %s: %s", port_name, flow_error.message);
219  int ret = rte_flow_validate(port_id, &attr, pattern, action, &flow_error);
220  SCLogError("Error on rte_flow validation for port %s: %s errmsg: %s", port_name,
221  rte_strerror(-ret), flow_error.message);
222  return ret;
223  } else {
224  SCLogInfo("RTE_FLOW flow rule created for port %s", port_name);
225  }
226 
227  return 0;
228 }
229 
230 static int i40eDeviceSetRSSFlowIPv4(
231  int port_id, const char *port_name, struct rte_eth_rss_conf rss_conf)
232 {
233  int ret = 0;
234  struct rte_flow_item pattern[] = { { 0 }, { 0 }, { 0 }, { 0 } };
235 
236  pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH;
237  pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV4;
238  pattern[2].type = RTE_FLOW_ITEM_TYPE_END;
239  ret |= i40eDeviceCreateRSSFlow(
240  port_id, port_name, rss_conf, RTE_ETH_RSS_NONFRAG_IPV4_OTHER, 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_IPV4;
245  pattern[2].type = RTE_FLOW_ITEM_TYPE_UDP;
246  pattern[3].type = RTE_FLOW_ITEM_TYPE_END;
247  ret |= i40eDeviceCreateRSSFlow(
248  port_id, port_name, rss_conf, RTE_ETH_RSS_NONFRAG_IPV4_UDP, pattern);
249  memset(pattern, 0, sizeof(pattern));
250 
251  pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH;
252  pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV4;
253  pattern[2].type = RTE_FLOW_ITEM_TYPE_TCP;
254  pattern[3].type = RTE_FLOW_ITEM_TYPE_END;
255  ret |= i40eDeviceCreateRSSFlow(
256  port_id, port_name, rss_conf, RTE_ETH_RSS_NONFRAG_IPV4_TCP, pattern);
257  memset(pattern, 0, sizeof(pattern));
258 
259  pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH;
260  pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV4;
261  pattern[2].type = RTE_FLOW_ITEM_TYPE_SCTP;
262  pattern[3].type = RTE_FLOW_ITEM_TYPE_END;
263  ret |= i40eDeviceCreateRSSFlow(
264  port_id, port_name, rss_conf, RTE_ETH_RSS_NONFRAG_IPV4_SCTP, pattern);
265  memset(pattern, 0, sizeof(pattern));
266 
267  pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH;
268  pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV4;
269  pattern[2].type = RTE_FLOW_ITEM_TYPE_END;
270  ret |= i40eDeviceCreateRSSFlow(port_id, port_name, rss_conf, RTE_ETH_RSS_FRAG_IPV4, pattern);
271 
272  return ret;
273 }
274 
275 static int i40eDeviceSetRSSFlowIPv6(
276  int port_id, const char *port_name, struct rte_eth_rss_conf rss_conf)
277 {
278  int ret = 0;
279  struct rte_flow_item pattern[] = { { 0 }, { 0 }, { 0 }, { 0 } };
280 
281  pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH;
282  pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV6;
283  pattern[2].type = RTE_FLOW_ITEM_TYPE_END;
284  ret |= i40eDeviceCreateRSSFlow(
285  port_id, port_name, rss_conf, RTE_ETH_RSS_NONFRAG_IPV6_OTHER, pattern);
286  memset(pattern, 0, sizeof(pattern));
287 
288  pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH;
289  pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV6;
290  pattern[2].type = RTE_FLOW_ITEM_TYPE_UDP;
291  pattern[3].type = RTE_FLOW_ITEM_TYPE_END;
292  ret |= i40eDeviceCreateRSSFlow(
293  port_id, port_name, rss_conf, RTE_ETH_RSS_NONFRAG_IPV6_UDP, pattern);
294  memset(pattern, 0, sizeof(pattern));
295 
296  pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH;
297  pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV6;
298  pattern[2].type = RTE_FLOW_ITEM_TYPE_TCP;
299  pattern[3].type = RTE_FLOW_ITEM_TYPE_END;
300  ret |= i40eDeviceCreateRSSFlow(
301  port_id, port_name, rss_conf, RTE_ETH_RSS_NONFRAG_IPV6_TCP, pattern);
302  memset(pattern, 0, sizeof(pattern));
303 
304  pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH;
305  pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV6;
306  pattern[2].type = RTE_FLOW_ITEM_TYPE_SCTP;
307  pattern[3].type = RTE_FLOW_ITEM_TYPE_END;
308  ret |= i40eDeviceCreateRSSFlow(
309  port_id, port_name, rss_conf, RTE_ETH_RSS_NONFRAG_IPV6_SCTP, pattern);
310  memset(pattern, 0, sizeof(pattern));
311 
312  pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH;
313  pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV6;
314  pattern[2].type = RTE_FLOW_ITEM_TYPE_END;
315  ret |= i40eDeviceCreateRSSFlow(port_id, port_name, rss_conf, RTE_ETH_RSS_FRAG_IPV6, pattern);
316 
317  return ret;
318 }
319 
320 static int i40eDeviceSetRSSWithFlows(int port_id, const char *port_name, int nb_rx_queues)
321 {
322  int retval;
323  uint8_t rss_key[I40E_RSS_HKEY_LEN];
324  struct rte_flow_error flush_error = { 0 };
325  struct rte_eth_rss_conf rss_conf = {
326  .rss_key = rss_key,
327  .rss_key_len = I40E_RSS_HKEY_LEN,
328  };
329 
330  retval = rte_eth_dev_rss_hash_conf_get(port_id, &rss_conf);
331  if (retval != 0) {
332  SCLogError("Unable to get RSS hash configuration of port %s", port_name);
333  return retval;
334  }
335 
336  retval = 0;
337  retval |= i40eDeviceSetRSSFlowQueues(port_id, port_name, rss_conf, nb_rx_queues);
338  retval |= i40eDeviceSetRSSFlowIPv4(port_id, port_name, rss_conf);
339  retval |= i40eDeviceSetRSSFlowIPv6(port_id, port_name, rss_conf);
340  if (retval != 0) {
341  retval = rte_flow_flush(port_id, &flush_error);
342  if (retval != 0) {
343  SCLogError("Unable to flush rte_flow rules of %s: %s Flush error msg: %s", port_name,
344  rte_strerror(-retval), flush_error.message);
345  }
346  return retval;
347  }
348 
349  return 0;
350 }
351 
352 #endif /* RTE_VER_YEAR < 19 */
353 
354 int i40eDeviceSetRSS(int port_id, int nb_rx_queues)
355 {
356  int retval;
357  (void)nb_rx_queues; // avoid unused variable warnings
358  char port_name[RTE_ETH_NAME_MAX_LEN];
359 
360  retval = rte_eth_dev_get_name_by_port(port_id, port_name);
361  if (unlikely(retval != 0)) {
362  SCLogError("Failed to convert port id %d to the interface name: %s", port_id,
363  strerror(-retval));
364  return retval;
365  }
366 
367 #if RTE_VER_YEAR <= 19
368  i40eDeviceSetRSSWithFilter(port_id, port_name);
369 #else
370  i40eDeviceSetRSSWithFlows(port_id, port_name, nb_rx_queues);
371 #endif
372  return 0;
373 }
374 
375 void i40eDeviceSetRSSHashFunction(uint64_t *rss_hf)
376 {
377  if (RTE_VER_YEAR <= 19)
378  *rss_hf = RTE_ETH_RSS_FRAG_IPV4 | RTE_ETH_RSS_NONFRAG_IPV4_TCP |
379  RTE_ETH_RSS_NONFRAG_IPV4_UDP | RTE_ETH_RSS_NONFRAG_IPV4_SCTP |
380  RTE_ETH_RSS_NONFRAG_IPV4_OTHER | RTE_ETH_RSS_FRAG_IPV6 |
381  RTE_ETH_RSS_NONFRAG_IPV6_TCP | RTE_ETH_RSS_NONFRAG_IPV6_UDP |
382  RTE_ETH_RSS_NONFRAG_IPV6_SCTP | RTE_ETH_RSS_NONFRAG_IPV6_OTHER | RTE_ETH_RSS_SCTP;
383  else
384  *rss_hf = RTE_ETH_RSS_FRAG_IPV4 | RTE_ETH_RSS_NONFRAG_IPV4_OTHER | RTE_ETH_RSS_FRAG_IPV6 |
385  RTE_ETH_RSS_NONFRAG_IPV6_OTHER;
386 }
387 
388 #endif /* HAVE_DPDK */
389 /**
390  * @}
391  */
offset
uint64_t offset
Definition: util-streaming-buffer.h:0
unlikely
#define unlikely(expr)
Definition: util-optimize.h:35
util-debug.h
SCLogInfo
#define SCLogInfo(...)
Macro used to log INFORMATIONAL messages.
Definition: util-debug.h:224
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-i40e.h