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