tls: handle disconect and reset in async mode
[vpp.git] / src / plugins / tlsopenssl / tls_async.c
1 /*
2  * Copyright (c) 2018 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/api_errno.h>
17 #include <vlib/node_funcs.h>
18 #include <openssl/engine.h>
19 #include <tlsopenssl/tls_openssl.h>
20
21 #define SSL_ASYNC_INFLIGHT    1
22 #define SSL_ASYNC_READY       2
23 #define SSL_ASYNC_REENTER     3
24 #define MAX_VECTOR_ASYNC    256
25
26 typedef struct openssl_tls_callback_arg_
27 {
28   int thread_index;
29   int event_index;
30 } openssl_tls_callback_arg_t;
31
32 typedef struct openssl_event_
33 {
34   u32 ctx_index;
35   int session_index;
36   u8 status;
37
38   openssl_resume_handler *handler;
39   openssl_tls_callback_arg_t cb_args;
40 #define thread_idx cb_args.thread_index
41 #define event_idx cb_args.event_index
42   int next;
43 } openssl_evt_t;
44
45 typedef struct openssl_async_queue_
46 {
47   int evt_run_head;
48   int evt_run_tail;
49 } openssl_async_queue_t;
50
51 typedef struct openssl_async_
52 {
53   openssl_evt_t ***evt_pool;
54   openssl_async_queue_t *queue;
55   void (*polling) (void);
56   u8 start_polling;
57   ENGINE *engine;
58
59 } openssl_async_t;
60
61 void qat_polling ();
62 void qat_pre_init ();
63 void qat_polling_config ();
64 void dasync_polling ();
65
66 struct engine_polling
67 {
68   char *engine;
69   void (*polling) (void);
70   void (*pre_init) (void);
71   void (*thread_init) (void *);
72 };
73
74 void qat_init_thread (void *arg);
75
76 struct engine_polling engine_list[] = {
77   {"qat", qat_polling, qat_pre_init, qat_init_thread},
78   {"dasync", dasync_polling, NULL, NULL}
79 };
80
81 openssl_async_t openssl_async_main;
82 static vlib_node_registration_t tls_async_process_node;
83
84 /* to avoid build warning */
85 void session_send_rpc_evt_to_thread (u32 thread_index, void *fp,
86                                      void *rpc_args);
87
88 void
89 evt_pool_init (vlib_main_t * vm)
90 {
91   vlib_thread_main_t *vtm = vlib_get_thread_main ();
92   openssl_async_t *om = &openssl_async_main;
93   int i, num_threads;
94
95   num_threads = 1 /* main thread */  + vtm->n_threads;
96
97   TLS_DBG (2, "Totally there is %d thread\n", num_threads);
98
99   vec_validate (om->evt_pool, num_threads - 1);
100   vec_validate (om->queue, num_threads - 1);
101
102   om->start_polling = 0;
103   om->engine = 0;
104
105   for (i = 0; i < num_threads; i++)
106     {
107       om->queue[i].evt_run_head = -1;
108       om->queue[i].evt_run_tail = -1;
109     }
110   om->polling = NULL;
111
112   return;
113 }
114
115 int
116 openssl_engine_register (char *engine_name, char *algorithm, int async)
117 {
118   int i, registered = -1;
119   openssl_async_t *om = &openssl_async_main;
120   void (*p) (void);
121   ENGINE *engine;
122
123   for (i = 0; i < ARRAY_LEN (engine_list); i++)
124     {
125       if (!strcmp (engine_list[i].engine, engine_name))
126         {
127           om->polling = engine_list[i].polling;
128           registered = i;
129         }
130     }
131   if (registered < 0)
132     {
133       clib_error ("engine %s is not regisered in VPP", engine_name);
134       return -1;
135     }
136
137   ENGINE_load_builtin_engines ();
138   ENGINE_load_dynamic ();
139   engine = ENGINE_by_id (engine_name);
140
141   if (engine == NULL)
142     {
143       clib_warning ("Failed to find engine ENGINE_by_id %s", engine_name);
144       return -1;
145     }
146
147   om->engine = engine;
148   /* call pre-init */
149   p = engine_list[registered].pre_init;
150   if (p)
151     (*p) ();
152
153   if (algorithm)
154     {
155       if (!ENGINE_set_default_string (engine, algorithm))
156         {
157           clib_warning ("Failed to set engine %s algorithm %s\n",
158                         engine_name, algorithm);
159           return -1;
160         }
161     }
162   else
163     {
164       if (!ENGINE_set_default (engine, ENGINE_METHOD_ALL))
165         {
166           clib_warning ("Failed to set engine %s to all algorithm",
167                         engine_name);
168           return -1;
169         }
170     }
171
172   if (async)
173     {
174       openssl_async_node_enable_disable (1);
175     }
176
177   for (i = 0; i < vlib_num_workers (); i++)
178     {
179       if (engine_list[registered].thread_init)
180         session_send_rpc_evt_to_thread (i + 1,
181                                         engine_list[registered].thread_init,
182                                         (void *) &i);
183     }
184
185   om->start_polling = 1;
186
187   return 0;
188
189 }
190
191 static openssl_evt_t *
192 openssl_evt_get (u32 evt_index)
193 {
194   openssl_evt_t **evt;
195   evt =
196     pool_elt_at_index (openssl_async_main.evt_pool[vlib_get_thread_index ()],
197                        evt_index);
198   return *evt;
199 }
200
201 static openssl_evt_t *
202 openssl_evt_get_w_thread (int evt_index, u8 thread_index)
203 {
204   openssl_evt_t **evt;
205
206   evt =
207     pool_elt_at_index (openssl_async_main.evt_pool[thread_index], evt_index);
208   return *evt;
209 }
210
211 int
212 openssl_evt_free (int event_index, u8 thread_index)
213 {
214   openssl_async_t *om = &openssl_async_main;
215
216   /*pool operation */
217   pool_put_index (om->evt_pool[thread_index], event_index);
218
219   return 1;
220 }
221
222 static u32
223 openssl_evt_alloc (void)
224 {
225   u8 thread_index = vlib_get_thread_index ();
226   openssl_async_t *tm = &openssl_async_main;
227   openssl_evt_t **evt;
228
229   pool_get (tm->evt_pool[thread_index], evt);
230   if (!(*evt))
231     *evt = clib_mem_alloc (sizeof (openssl_evt_t));
232
233   clib_memset (*evt, 0, sizeof (openssl_evt_t));
234   (*evt)->event_idx = evt - tm->evt_pool[thread_index];
235   return ((*evt)->event_idx);
236 }
237
238
239 /* In most cases, tls_async_openssl_callback is called by HW to make event active
240  * When EAGAIN received, VPP will call this callback to retry
241  */
242 int
243 tls_async_openssl_callback (SSL * s, void *cb_arg)
244 {
245   openssl_evt_t *event, *event_tail;
246   openssl_async_t *om = &openssl_async_main;
247   openssl_tls_callback_arg_t *args = (openssl_tls_callback_arg_t *) cb_arg;
248   int thread_index = args->thread_index;
249   int event_index = args->event_index;
250   int *evt_run_tail = &om->queue[thread_index].evt_run_tail;
251   int *evt_run_head = &om->queue[thread_index].evt_run_head;
252
253   TLS_DBG (2, "Set event %d to run\n", event_index);
254
255   event = openssl_evt_get (event_index);
256
257   /* Happend when a recursive case, especially in SW simulation */
258   if (PREDICT_FALSE (event->status == SSL_ASYNC_READY))
259     {
260       event->status = SSL_ASYNC_REENTER;
261       return 0;
262     }
263   event->status = SSL_ASYNC_READY;
264   event->next = -1;
265
266   if (*evt_run_tail >= 0)
267     {
268       event_tail = openssl_evt_get_w_thread (*evt_run_tail, thread_index);
269       event_tail->next = event_index;
270     }
271   *evt_run_tail = event_index;
272   if (*evt_run_head < 0)
273     {
274       *evt_run_head = event_index;
275     }
276
277   return 1;
278 }
279
280 int
281 vpp_tls_async_init_event (tls_ctx_t * ctx,
282                           openssl_resume_handler * handler,
283                           session_t * session)
284 {
285   u32 eidx;
286   openssl_evt_t *event;
287   openssl_ctx_t *oc = (openssl_ctx_t *) ctx;
288   u32 thread_id = ctx->c_thread_index;
289
290   eidx = openssl_evt_alloc ();
291   event = openssl_evt_get (eidx);
292   event->ctx_index = oc->openssl_ctx_index;
293   event->event_idx = eidx;
294   event->thread_idx = thread_id;
295   event->handler = handler;
296   event->session_index = session->session_index;
297   event->status = 0;
298   ctx->evt_index = eidx;
299 #ifdef HAVE_OPENSSL_ASYNC
300   SSL_set_async_callback_arg (oc->ssl, &event->cb_args);
301 #endif
302
303   return 1;
304 }
305
306 int
307 vpp_openssl_is_inflight (tls_ctx_t * ctx)
308 {
309   u32 eidx;
310   openssl_evt_t *event;
311   eidx = ctx->evt_index;
312   event = openssl_evt_get (eidx);
313
314   if (event->status == SSL_ASYNC_INFLIGHT)
315     return 1;
316   return 0;
317 }
318
319 int
320 vpp_tls_async_update_event (tls_ctx_t * ctx, int eagain)
321 {
322   u32 eidx;
323   openssl_evt_t *event;
324
325   eidx = ctx->evt_index;
326   event = openssl_evt_get (eidx);
327   event->status = SSL_ASYNC_INFLIGHT;
328   if (eagain)
329     return tls_async_openssl_callback (0, &event->cb_args);
330
331   return 1;
332 }
333
334 void
335 event_handler (void *tls_async)
336 {
337   openssl_resume_handler *handler;
338   openssl_evt_t *event;
339   session_t *session;
340   int thread_index;
341   tls_ctx_t *ctx;
342
343   event = (openssl_evt_t *) tls_async;
344   thread_index = event->thread_idx;
345   ctx = openssl_ctx_get_w_thread (event->ctx_index, thread_index);
346   handler = event->handler;
347   session = session_get (event->session_index, thread_index);
348
349   if (handler)
350     {
351       (*handler) (ctx, session);
352     }
353
354   return;
355 }
356
357  /* engine specific code to polling the response ring */
358 void
359 dasync_polling ()
360 {
361 /* dasync is a fake async device, and could not be polled.
362  * We have added code in the dasync engine to triggered the callback already,
363  * so nothing can be done here
364  */
365 }
366
367 void
368 qat_pre_init ()
369 {
370   openssl_async_t *om = &openssl_async_main;
371
372   ENGINE_ctrl_cmd (om->engine, "ENABLE_EXTERNAL_POLLING", 0, NULL, NULL, 0);
373 }
374
375 /* Below code is spefic to QAT engine, and other vendors can refer to this code to enable a new engine */
376 void
377 qat_init_thread (void *arg)
378 {
379   openssl_async_t *om = &openssl_async_main;
380   int thread_index = *(int *) arg;
381
382   ENGINE_ctrl_cmd (om->engine, "SET_INSTANCE_FOR_THREAD", thread_index,
383                    NULL, NULL, 0);
384
385   TLS_DBG (2, "set thread %d and instance %d mapping\n", thread_index,
386            thread_index);
387
388 }
389
390 void
391 qat_polling ()
392 {
393   openssl_async_t *om = &openssl_async_main;
394   int poll_status = 0;
395
396   if (om->start_polling)
397     {
398       ENGINE_ctrl_cmd (om->engine, "POLL", 0, &poll_status, NULL, 0);
399     }
400 }
401
402 void
403 openssl_async_polling ()
404 {
405   openssl_async_t *om = &openssl_async_main;
406   if (om->polling)
407     {
408       (*om->polling) ();
409     }
410 }
411
412 void
413 openssl_async_node_enable_disable (u8 is_en)
414 {
415   u8 state = is_en ? VLIB_NODE_STATE_POLLING : VLIB_NODE_STATE_DISABLED;
416   vlib_thread_main_t *vtm = vlib_get_thread_main ();
417   u8 have_workers = vtm->n_threads != 0;
418
419   /* *INDENT-OFF* */
420   foreach_vlib_main (({
421     if (have_workers && ii != 0)
422       {
423         vlib_node_set_state (this_vlib_main, tls_async_process_node.index,
424                          state);
425       }
426   }));
427   /* *INDENT-ON* */
428 }
429
430 int
431 tls_async_do_job (int eidx, u32 thread_index)
432 {
433   tls_ctx_t *ctx;
434   openssl_evt_t *event;
435
436   /* do the real job */
437   event = openssl_evt_get_w_thread (eidx, thread_index);
438   ctx = openssl_ctx_get_w_thread (event->ctx_index, thread_index);
439
440   if (ctx)
441     {
442       ctx->resume = 1;
443       session_send_rpc_evt_to_thread (thread_index, event_handler, event);
444     }
445   return 1;
446 }
447
448 int
449 tls_resume_from_crypto (int thread_index)
450 {
451   int i;
452
453   openssl_async_t *om = &openssl_async_main;
454   openssl_evt_t *event;
455   int *evt_run_head = &om->queue[thread_index].evt_run_head;
456   int *evt_run_tail = &om->queue[thread_index].evt_run_tail;
457
458   if (*evt_run_head < 0)
459     return 0;
460
461   for (i = 0; i < MAX_VECTOR_ASYNC; i++)
462     {
463       if (*evt_run_head >= 0)
464         {
465           event = openssl_evt_get_w_thread (*evt_run_head, thread_index);
466           tls_async_do_job (*evt_run_head, thread_index);
467           if (PREDICT_FALSE (event->status == SSL_ASYNC_REENTER))
468             {
469               /* recusive event triggered */
470               event->status = SSL_ASYNC_READY;
471               continue;
472             }
473
474           event->status = 0;
475           *evt_run_head = event->next;
476
477           if (event->next < 0)
478             {
479               *evt_run_tail = -1;
480               break;
481             }
482         }
483     }
484
485   return 0;
486
487 }
488
489 static clib_error_t *
490 tls_async_init (vlib_main_t * vm)
491 {
492   evt_pool_init (vm);
493   return 0;
494 }
495
496 static uword
497 tls_async_process (vlib_main_t * vm, vlib_node_runtime_t * rt,
498                    vlib_frame_t * f)
499 {
500   u8 thread_index;
501   openssl_async_t *om = &openssl_async_main;
502
503   thread_index = vlib_get_thread_index ();
504   if (pool_elts (om->evt_pool[thread_index]) > 0)
505     {
506       openssl_async_polling ();
507       tls_resume_from_crypto (thread_index);
508     }
509
510   return 0;
511 }
512
513 VLIB_INIT_FUNCTION (tls_async_init);
514
515 /* *INDENT-OFF* */
516 VLIB_REGISTER_NODE (tls_async_process_node,static) = {
517     .function = tls_async_process,
518     .type = VLIB_NODE_TYPE_INPUT,
519     .name = "tls-async-process",
520     .state = VLIB_NODE_STATE_DISABLED,
521 };
522
523 /* *INDENT-ON* */
524
525 /*
526  * fd.io coding-style-patch-verification: ON
527  *
528  * Local Variables:
529  * eval: (c-set-style "gnu")
530  * End:
531  */