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