FD.io VPP  v17.10-9-gd594711
Vector Packet Processing
ipsec.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2016 Intel and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #include <vnet/vnet.h>
16 #include <vnet/ip/ip.h>
17 #include <vnet/api_errno.h>
18 #include <vnet/ipsec/ipsec.h>
19 #include <vlib/node_funcs.h>
20 
21 #include <dpdk/device/dpdk.h>
22 #include <dpdk/ipsec/ipsec.h>
23 #include <dpdk/ipsec/esp.h>
24 
25 #define DPDK_CRYPTO_NB_SESS_OBJS 20000
26 #define DPDK_CRYPTO_CACHE_SIZE 512
27 #define DPDK_CRYPTO_PRIV_SIZE 128
28 #define DPDK_CRYPTO_N_QUEUE_DESC 1024
29 #define DPDK_CRYPTO_NB_COPS (1024 * 4)
30 
31 static int
32 add_del_sa_sess (u32 sa_index, u8 is_add)
33 {
36  u8 skip_master = vlib_num_workers () > 0;
37 
38  /* *INDENT-OFF* */
39  vec_foreach (cwm, dcm->workers_main)
40  {
41  crypto_sa_session_t *sa_sess;
42  u8 is_outbound;
43 
44  if (skip_master)
45  {
46  skip_master = 0;
47  continue;
48  }
49 
50  for (is_outbound = 0; is_outbound < 2; is_outbound++)
51  {
52  if (is_add)
53  {
54  pool_get (cwm->sa_sess_d[is_outbound], sa_sess);
55  }
56  else
57  {
58  u8 dev_id;
59  i32 ret;
60 
61  sa_sess = pool_elt_at_index (cwm->sa_sess_d[is_outbound], sa_index);
62  dev_id = cwm->qp_data[sa_sess->qp_index].dev_id;
63 
64  if (!sa_sess->sess)
65  continue;
66 #if DPDK_NO_AEAD
67  ret = (rte_cryptodev_sym_session_free(dev_id, sa_sess->sess) == NULL);
68  ASSERT (ret);
69 #else
70  ret = rte_cryptodev_sym_session_clear(dev_id, sa_sess->sess);
71  ASSERT (!ret);
72 
73  ret = rte_cryptodev_sym_session_free(sa_sess->sess);
74  ASSERT (!ret);
75 #endif
76  memset(sa_sess, 0, sizeof(sa_sess[0]));
77  }
78  }
79  }
80  /* *INDENT-OFF* */
81 
82  return 0;
83 }
84 
85 static void
87  u8 cdev_id, u16 qp_id, u8 is_outbound, u16 * idx)
88 {
89  crypto_qp_data_t *qpd;
90 
91  /* *INDENT-OFF* */
92  vec_foreach_index (*idx, cwm->qp_data)
93  {
94  qpd = vec_elt_at_index(cwm->qp_data, *idx);
95 
96  if (qpd->dev_id == cdev_id && qpd->qp_id == qp_id &&
97  qpd->is_outbound == is_outbound)
98  return;
99  }
100  /* *INDENT-ON* */
101 
103 
104  qpd->dev_id = cdev_id;
105  qpd->qp_id = qp_id;
106  qpd->is_outbound = is_outbound;
107 }
108 
109 /*
110  * return:
111  * 0: already exist
112  * 1: mapped
113  */
114 static int
116  u8 cdev_id, u16 qp, u8 is_outbound,
117  const struct rte_cryptodev_capabilities *cipher_cap,
118  const struct rte_cryptodev_capabilities *auth_cap)
119 {
120  u16 qp_index;
121  uword key = 0, data, *ret;
123 
124  p_key->cipher_algo = (u8) cipher_cap->sym.cipher.algo;
125  p_key->auth_algo = (u8) auth_cap->sym.auth.algo;
126  p_key->is_outbound = is_outbound;
127 #if ! DPDK_NO_AEAD
128  p_key->is_aead = cipher_cap->sym.xform_type == RTE_CRYPTO_SYM_XFORM_AEAD;
129 #endif
130 
131  ret = hash_get (cwm->algo_qp_map, key);
132  if (ret)
133  return 0;
134 
135  update_qp_data (cwm, cdev_id, qp, is_outbound, &qp_index);
136 
137  data = (uword) qp_index;
138  hash_set (cwm->algo_qp_map, key, data);
139 
140  return 1;
141 }
142 
143 /*
144  * return:
145  * 0: already exist
146  * 1: mapped
147  */
148 static int
150  struct rte_cryptodev_info *dev_info, u8 cdev_id,
151  u16 qp, u8 is_outbound)
152 {
153  const struct rte_cryptodev_capabilities *i, *j;
154  u32 mapped = 0;
155 
156  for (i = dev_info->capabilities; i->op != RTE_CRYPTO_OP_TYPE_UNDEFINED; i++)
157  {
158 #if ! DPDK_NO_AEAD
159  if (i->sym.xform_type == RTE_CRYPTO_SYM_XFORM_AEAD)
160  {
161  struct rte_cryptodev_capabilities none = { 0 };
162 
163  if (check_algo_is_supported (i, NULL) != 0)
164  continue;
165 
166  none.sym.auth.algo = RTE_CRYPTO_AUTH_NULL;
167 
168  mapped |= add_mapping (cwm, cdev_id, qp, is_outbound, i, &none);
169  continue;
170  }
171 #endif
172  if (i->sym.xform_type != RTE_CRYPTO_SYM_XFORM_CIPHER)
173  continue;
174 
175  if (check_algo_is_supported (i, NULL) != 0)
176  continue;
177 
178  for (j = dev_info->capabilities; j->op != RTE_CRYPTO_OP_TYPE_UNDEFINED;
179  j++)
180  {
181  if (j->sym.xform_type != RTE_CRYPTO_SYM_XFORM_AUTH)
182  continue;
183 
184  if (check_algo_is_supported (j, NULL) != 0)
185  continue;
186 
187  mapped |= add_mapping (cwm, cdev_id, qp, is_outbound, i, j);
188  }
189  }
190 
191  return mapped;
192 }
193 
194 static int
196 {
197  u32 n_qs = 0;
198  u8 cdev_id;
199  u32 n_req_qs = 2;
200 
201  if (vlib_num_workers () > 0)
202  n_req_qs = vlib_num_workers () * 2;
203 
204  for (cdev_id = 0; cdev_id < rte_cryptodev_count (); cdev_id++)
205  {
206  struct rte_cryptodev_info cdev_info;
207 
208  rte_cryptodev_info_get (cdev_id, &cdev_info);
209 
210  if (!
211  (cdev_info.feature_flags & RTE_CRYPTODEV_FF_SYM_OPERATION_CHAINING))
212  continue;
213 
214  n_qs += cdev_info.max_nb_queue_pairs;
215  }
216 
217  if (n_qs >= n_req_qs)
218  return 0;
219  else
220  return -1;
221 }
222 
223 static clib_error_t *
225 {
226  if (sa->crypto_alg == IPSEC_CRYPTO_ALG_AES_GCM_128)
227  {
228  if (sa->integ_alg != IPSEC_INTEG_ALG_NONE)
229  return clib_error_return (0, "unsupported integ-alg %U with "
230  "crypto-alg aes-gcm-128",
232 #if DPDK_NO_AEAD
233  sa->integ_alg = IPSEC_INTEG_ALG_AES_GCM_128;
234 #endif
235  }
236 #if DPDK_NO_AEAD
237  else if (sa->crypto_alg == IPSEC_CRYPTO_ALG_NONE ||
238  sa->integ_alg == IPSEC_INTEG_ALG_NONE ||
239  sa->integ_alg == IPSEC_INTEG_ALG_AES_GCM_128)
240 #else
241  else if (sa->integ_alg == IPSEC_INTEG_ALG_NONE)
242 #endif
243  return clib_error_return (0,
244  "unsupported integ-alg %U with crypto-alg %U",
247 
248  return 0;
249 }
250 
251 static uword
253  vlib_frame_t * f)
254 {
255  ipsec_main_t *im = &ipsec_main;
258  struct rte_cryptodev_config dev_conf;
259  struct rte_cryptodev_qp_conf qp_conf;
260  struct rte_cryptodev_info cdev_info;
261  struct rte_mempool *rmp;
262  i32 dev_id, ret;
263  u32 i, skip_master;
264 #if ! DPDK_NO_AEAD
265  u32 max_sess_size = 0, sess_size;
266  i8 socket_id;
267 #endif
268 
269  if (check_cryptodev_queues () < 0)
270  {
271  clib_warning ("not enough Cryptodevs, default to OpenSSL IPsec");
272  return 0;
273  }
274  dcm->enabled = 1;
275 
276  vec_alloc (dcm->workers_main, tm->n_vlib_mains);
277  _vec_len (dcm->workers_main) = tm->n_vlib_mains;
278 
279  skip_master = vlib_num_workers () > 0;
280 
281  fprintf (stdout, "DPDK Cryptodevs info:\n");
282  fprintf (stdout, "dev_id\tn_qp\tnb_obj\tcache_size\n");
283  /* HW cryptodevs have higher dev_id, use HW first */
284  for (dev_id = rte_cryptodev_count () - 1; dev_id >= 0; dev_id--)
285  {
286  u16 max_nb_qp, qp = 0;
287 
288  rte_cryptodev_info_get (dev_id, &cdev_info);
289 
290  if (!
291  (cdev_info.feature_flags & RTE_CRYPTODEV_FF_SYM_OPERATION_CHAINING))
292  continue;
293 
294  max_nb_qp = cdev_info.max_nb_queue_pairs;
295 
296  for (i = skip_master; i < tm->n_vlib_mains; i++)
297  {
298  u8 is_outbound;
300  uword *map;
301 
302  cwm = vec_elt_at_index (dcm->workers_main, i);
303  map = cwm->algo_qp_map;
304 
305  if (!map)
306  {
307  map = hash_create (0, sizeof (crypto_worker_qp_key_t));
308  if (!map)
309  {
310  clib_warning ("unable to create hash table for worker %u",
311  vlib_mains[i]->thread_index);
312  goto error;
313  }
314  cwm->algo_qp_map = map;
315  }
316 
317  for (is_outbound = 0; is_outbound < 2 && qp < max_nb_qp;
318  is_outbound++)
319  qp += add_cdev_mapping (cwm, &cdev_info, dev_id, qp, is_outbound);
320  }
321 
322  if (qp == 0)
323  continue;
324 
325  dev_conf.socket_id = rte_cryptodev_socket_id (dev_id);
326  dev_conf.nb_queue_pairs = cdev_info.max_nb_queue_pairs;
327 #if DPDK_NO_AEAD
328  dev_conf.session_mp.nb_objs = DPDK_CRYPTO_NB_SESS_OBJS;
329  dev_conf.session_mp.cache_size = DPDK_CRYPTO_CACHE_SIZE;
330 #endif
331  ret = rte_cryptodev_configure (dev_id, &dev_conf);
332  if (ret < 0)
333  {
334  clib_warning ("cryptodev %u config error", dev_id);
335  goto error;
336  }
337 
338  qp_conf.nb_descriptors = DPDK_CRYPTO_N_QUEUE_DESC;
339  for (qp = 0; qp < dev_conf.nb_queue_pairs; qp++)
340  {
341 #if DPDK_NO_AEAD
342  ret = rte_cryptodev_queue_pair_setup (dev_id, qp, &qp_conf,
343  dev_conf.socket_id);
344 #else
345  ret = rte_cryptodev_queue_pair_setup (dev_id, qp, &qp_conf,
346  dev_conf.socket_id, NULL);
347 #endif
348  if (ret < 0)
349  {
350  clib_warning ("cryptodev %u qp %u setup error", dev_id, qp);
351  goto error;
352  }
353  }
354  vec_validate (dcm->cop_pools, dev_conf.socket_id);
355 
356 #if ! DPDK_NO_AEAD
357  sess_size = rte_cryptodev_get_private_session_size (dev_id);
358  if (sess_size > max_sess_size)
359  max_sess_size = sess_size;
360 #endif
361 
362  if (!vec_elt (dcm->cop_pools, dev_conf.socket_id))
363  {
364  u8 *pool_name = format (0, "crypto_op_pool_socket%u%c",
365  dev_conf.socket_id, 0);
366 
367  rmp = rte_crypto_op_pool_create ((char *) pool_name,
368  RTE_CRYPTO_OP_TYPE_SYMMETRIC,
370  (1 + vlib_num_workers ()),
373  dev_conf.socket_id);
374 
375  if (!rmp)
376  {
377  clib_warning ("failed to allocate %s", pool_name);
378  vec_free (pool_name);
379  goto error;
380  }
381  vec_free (pool_name);
382  vec_elt (dcm->cop_pools, dev_conf.socket_id) = rmp;
383  }
384 
385  fprintf (stdout, "%u\t%u\t%u\t%u\n", dev_id, dev_conf.nb_queue_pairs,
387  }
388 
389 #if ! DPDK_NO_AEAD
390  /* *INDENT-OFF* */
391  vec_foreach_index (socket_id, dcm->cop_pools)
392  {
393  u8 *pool_name;
394 
395  if (!vec_elt (dcm->cop_pools, socket_id))
396  continue;
397 
398  vec_validate (dcm->sess_h_pools, socket_id);
399  pool_name = format (0, "crypto_sess_h_socket%u%c",
400  socket_id, 0);
401  rmp =
402  rte_mempool_create((i8 *)pool_name, DPDK_CRYPTO_NB_SESS_OBJS,
403  rte_cryptodev_get_header_session_size (),
404  512, 0, NULL, NULL, NULL, NULL,
405  socket_id, 0);
406  if (!rmp)
407  {
408  clib_warning ("failed to allocate %s", pool_name);
409  vec_free (pool_name);
410  goto error;
411  }
412  vec_free (pool_name);
413  vec_elt (dcm->sess_h_pools, socket_id) = rmp;
414 
415  vec_validate (dcm->sess_pools, socket_id);
416  pool_name = format (0, "crypto_sess_socket%u%c",
417  socket_id, 0);
418  rmp =
419  rte_mempool_create((i8 *)pool_name, DPDK_CRYPTO_NB_SESS_OBJS,
420  max_sess_size, 512, 0, NULL, NULL, NULL, NULL,
421  socket_id, 0);
422  if (!rmp)
423  {
424  clib_warning ("failed to allocate %s", pool_name);
425  vec_free (pool_name);
426  goto error;
427  }
428  vec_free (pool_name);
429  vec_elt (dcm->sess_pools, socket_id) = rmp;
430  }
431  /* *INDENT-ON* */
432 #endif
433 
434  dpdk_esp_init ();
435 
436  /* Add new next node and set as default */
437  vlib_node_t *node, *next_node;
438 
439  next_node = vlib_get_node_by_name (vm, (u8 *) "dpdk-esp-encrypt");
440  ASSERT (next_node);
441  node = vlib_get_node_by_name (vm, (u8 *) "ipsec-output-ip4");
442  ASSERT (node);
443  im->esp_encrypt_node_index = next_node->index;
445  vlib_node_add_next (vm, node->index, next_node->index);
446 
447  next_node = vlib_get_node_by_name (vm, (u8 *) "dpdk-esp-decrypt");
448  ASSERT (next_node);
449  node = vlib_get_node_by_name (vm, (u8 *) "ipsec-input-ip4");
450  ASSERT (node);
451  im->esp_decrypt_node_index = next_node->index;
453  vlib_node_add_next (vm, node->index, next_node->index);
454 
457 
458  for (i = skip_master; i < tm->n_vlib_mains; i++)
460  VLIB_NODE_STATE_POLLING);
461 
462  /* TODO cryptodev counters */
463 
464  return 0;
465 
466 error:
467  ;
469  struct rte_mempool **mp;
470  /* *INDENT-OFF* */
471  vec_foreach (cwm, dcm->workers_main)
472  hash_free (cwm->algo_qp_map);
473 
474  vec_foreach (mp, dcm->cop_pools)
475  {
476  if (mp)
477  rte_mempool_free (mp[0]);
478  }
479  /* *INDENT-ON* */
480  vec_free (dcm->workers_main);
481  vec_free (dcm->cop_pools);
482 
483  return 0;
484 }
485 
486 /* *INDENT-OFF* */
488  .function = dpdk_ipsec_process,
489  .type = VLIB_NODE_TYPE_PROCESS,
490  .name = "dpdk-ipsec-process",
491  .process_log2_n_stack_bytes = 17,
492 };
493 /* *INDENT-ON* */
494 
495 /*
496  * fd.io coding-style-patch-verification: ON
497  *
498  * Local Variables:
499  * eval: (c-set-style "gnu")
500  * End:
501  */
#define vec_validate(V, I)
Make sure vector is long enough for given index (no header, unspecified alignment) ...
Definition: vec.h:432
static int add_mapping(crypto_worker_main_t *cwm, u8 cdev_id, u16 qp, u8 is_outbound, const struct rte_cryptodev_capabilities *cipher_cap, const struct rte_cryptodev_capabilities *auth_cap)
Definition: ipsec.c:115
#define vec_foreach_index(var, v)
Iterate over vector indices.
#define hash_set(h, key, value)
Definition: hash.h:254
sll srl srl sll sra u16x4 i
Definition: vector_sse2.h:337
i32(* add_del_sa_sess_cb)(u32 sa_index, u8 is_add)
Definition: ipsec.h:239
#define NULL
Definition: clib.h:55
u32 index
Definition: node.h:237
#define vec_add2_aligned(V, P, N, A)
Add N elements to end of vector V, return pointer to new elements in P.
Definition: vec.h:569
u16 is_outbound
Definition: ipsec.h:63
#define DPDK_CRYPTO_NB_SESS_OBJS
Definition: ipsec.c:25
ipsec_integ_alg_t integ_alg
Definition: ipsec.h:110
#define DPDK_CRYPTO_PRIV_SIZE
Definition: ipsec.c:27
u8 * format(u8 *s, const char *fmt,...)
Definition: format.c:419
struct rte_mempool ** sess_pools
Definition: ipsec.h:86
vlib_node_registration_t dpdk_crypto_input_node
(constructor) VLIB_REGISTER_NODE (dpdk_crypto_input_node)
Definition: crypto_node.c:60
#define pool_get(P, E)
Allocate an object E from a pool P (unspecified alignment).
Definition: pool.h:225
#define vec_alloc(V, N)
Allocate space for N more elements (no header, unspecified alignment)
Definition: vec.h:275
vlib_main_t ** vlib_mains
Definition: buffer.c:292
static uword vlib_node_add_next(vlib_main_t *vm, uword node, uword next_node)
Definition: node_funcs.h:1108
dpdk_crypto_main_t dpdk_crypto_main
Definition: ipsec.h:92
static void update_qp_data(crypto_worker_main_t *cwm, u8 cdev_id, u16 qp_id, u8 is_outbound, u16 *idx)
Definition: ipsec.c:86
static_always_inline void dpdk_esp_init()
Definition: esp.h:47
uword * algo_qp_map
Definition: ipsec.h:80
u32 esp_encrypt_next_index
Definition: ipsec.h:275
ipsec_main_t ipsec_main
Definition: ipsec.h:282
int i32
Definition: types.h:81
char i8
Definition: types.h:45
#define vec_elt_at_index(v, i)
Get vector value at index i checking that i is in bounds.
#define clib_error_return(e, args...)
Definition: error.h:99
ipsec_main_callbacks_t cb
Definition: ipsec.h:279
u8 * format_ipsec_crypto_alg(u8 *s, va_list *args)
Definition: ipsec_format.c:58
#define hash_get(h, key)
Definition: hash.h:248
#define pool_elt_at_index(p, i)
Returns pointer to element at given index.
Definition: pool.h:458
clib_error_t *(* check_support_cb)(ipsec_sa_t *sa)
Definition: ipsec.h:240
#define hash_free(h)
Definition: hash.h:286
#define DPDK_CRYPTO_NB_COPS
Definition: ipsec.c:29
static int check_cryptodev_queues()
Definition: ipsec.c:195
vlib node functions
vlib_main_t * vm
Definition: buffer.c:283
u32 esp_encrypt_node_index
Definition: ipsec.h:272
#define vec_free(V)
Free vector&#39;s memory (no header).
Definition: vec.h:336
u32 esp_decrypt_next_index
Definition: ipsec.h:276
#define clib_warning(format, args...)
Definition: error.h:59
static int add_del_sa_sess(u32 sa_index, u8 is_add)
Definition: ipsec.c:32
vlib_node_t * vlib_get_node_by_name(vlib_main_t *vm, u8 *name)
Definition: node.c:45
static int add_cdev_mapping(crypto_worker_main_t *cwm, struct rte_cryptodev_info *dev_info, u8 cdev_id, u16 qp, u8 is_outbound)
Definition: ipsec.c:149
#define hash_create(elts, value_bytes)
Definition: hash.h:658
#define ASSERT(truth)
unsigned int u32
Definition: types.h:88
struct rte_mempool ** cop_pools
Definition: ipsec.h:87
crypto_worker_main_t * workers_main
Definition: ipsec.h:88
#define DPDK_CRYPTO_CACHE_SIZE
Definition: ipsec.c:26
static void vlib_node_set_state(vlib_main_t *vm, u32 node_index, vlib_node_state_t new_state)
Set node dispatch state.
Definition: node_funcs.h:147
crypto_qp_data_t * qp_data
Definition: ipsec.h:79
crypto_sa_session_t * sa_sess_d[2]
Definition: ipsec.h:78
u64 uword
Definition: types.h:112
#define vec_elt(v, i)
Get vector value at index i.
u8 * format_ipsec_integ_alg(u8 *s, va_list *args)
Definition: ipsec_format.c:90
unsigned short u16
Definition: types.h:57
unsigned char u8
Definition: types.h:56
static vlib_node_registration_t dpdk_ipsec_process_node
(constructor) VLIB_REGISTER_NODE (dpdk_ipsec_process_node)
Definition: ipsec.c:487
static_always_inline int check_algo_is_supported(const struct rte_cryptodev_capabilities *cap, char *name)
Definition: ipsec.h:147
#define DPDK_CRYPTO_N_QUEUE_DESC
Definition: ipsec.c:28
ipsec_crypto_alg_t crypto_alg
Definition: ipsec.h:106
#define VLIB_REGISTER_NODE(x,...)
Definition: node.h:143
static vlib_thread_main_t * vlib_get_thread_main()
Definition: global_funcs.h:32
static u32 vlib_num_workers()
Definition: threads.h:368
static uword dpdk_ipsec_process(vlib_main_t *vm, vlib_node_runtime_t *rt, vlib_frame_t *f)
Definition: ipsec.c:252
#define vec_foreach(var, vec)
Vector iterator.
#define CLIB_CACHE_LINE_BYTES
Definition: cache.h:67
struct rte_mempool ** sess_h_pools
Definition: ipsec.h:85
static clib_error_t * dpdk_ipsec_check_support(ipsec_sa_t *sa)
Definition: ipsec.c:224
u32 esp_decrypt_node_index
Definition: ipsec.h:273