8b7c5b6374cdc476b3706f1099fa539c1f36c897
[vpp.git] / src / plugins / srtp / srtp.c
1 /*
2  * Copyright (c) 2021 Cisco 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
16 #include <srtp/srtp.h>
17 #include <vnet/session/application_interface.h>
18 #include <vnet/session/session.h>
19
20 static srtp_main_t srtp_main;
21
22 static void srtp_disconnect (u32 ctx_handle, u32 thread_index);
23 static void srtp_disconnect_transport (srtp_tc_t *ctx);
24
25 static inline u32
26 srtp_ctx_alloc_w_thread (u32 thread_index)
27 {
28   srtp_tc_t *ctx;
29   pool_get_aligned_safe (srtp_main.ctx_pool[thread_index], ctx,
30                          CLIB_CACHE_LINE_BYTES);
31   clib_memset (ctx, 0, sizeof (*ctx));
32   ctx->c_thread_index = thread_index;
33   ctx->srtp_ctx_handle = ctx - srtp_main.ctx_pool[thread_index];
34   ctx->app_session_handle = SESSION_INVALID_HANDLE;
35   return ctx->srtp_ctx_handle;
36 }
37
38 static inline srtp_tc_t *
39 srtp_ctx_get_w_thread (u32 ctx_index, u32 thread_index)
40 {
41   return pool_elt_at_index (srtp_main.ctx_pool[thread_index], ctx_index);
42 }
43
44 static void
45 srtp_init_policy (srtp_tc_t *ctx, transport_endpt_cfg_srtp_t *cfg)
46 {
47   transport_endpt_cfg_srtp_policy_t *sp_cfg;
48   srtp_policy_t *sp;
49   int i;
50
51   for (i = 0; i < 2; i++)
52     {
53       sp = &ctx->srtp_policy[i];
54       sp_cfg = &cfg->policies[i];
55       clib_memset (sp, 0, sizeof (*sp));
56
57       srtp_crypto_policy_set_rtp_default (&sp->rtp);
58       srtp_crypto_policy_set_rtcp_default (&sp->rtcp);
59       sp->ssrc.type = sp_cfg->ssrc_type;
60       sp->ssrc.value = sp_cfg->ssrc_value;
61       sp->key = clib_mem_alloc (sp_cfg->key_len);
62       clib_memcpy (sp->key, sp_cfg->key, sp_cfg->key_len);
63       sp->next = i < 1 ? &ctx->srtp_policy[i + 1] : 0;
64       sp->window_size = sp_cfg->window_size;
65       sp->allow_repeat_tx = sp_cfg->allow_repeat_tx;
66     }
67 }
68
69 static void
70 srtp_ctx_free_policy (srtp_tc_t *ctx)
71 {
72   clib_mem_free (ctx->srtp_policy[0].key);
73   clib_mem_free (ctx->srtp_policy[1].key);
74 }
75
76 void
77 srtp_ctx_free (srtp_tc_t *ctx)
78 {
79   if (!ctx->is_migrated)
80     srtp_ctx_free_policy (ctx);
81   pool_put (srtp_main.ctx_pool[ctx->c_thread_index], ctx);
82 }
83
84 static inline u32
85 srtp_ctx_attach (u32 thread_index, void *ctx_ptr)
86 {
87   srtp_tc_t *ctx;
88
89   pool_get_aligned_safe (srtp_main.ctx_pool[thread_index], ctx,
90                          CLIB_CACHE_LINE_BYTES);
91   clib_memcpy (ctx, ctx_ptr, sizeof (*ctx));
92
93   ctx->c_thread_index = thread_index;
94   ctx->srtp_ctx_handle = ctx - srtp_main.ctx_pool[thread_index];
95   return 0;
96 }
97
98 static inline void *
99 srtp_ctx_detach (srtp_tc_t *ctx)
100 {
101   srtp_tc_t *sc;
102
103   sc = clib_mem_alloc (sizeof (*sc));
104   clib_memcpy (sc, ctx, sizeof (*sc));
105
106   return sc;
107 }
108
109 u32
110 srtp_listener_ctx_alloc (void)
111 {
112   srtp_main_t *sm = &srtp_main;
113   srtp_tc_t *ctx;
114
115   pool_get_zero (sm->listener_ctx_pool, ctx);
116   return ctx - sm->listener_ctx_pool;
117 }
118
119 void
120 srtp_listener_ctx_free (srtp_tc_t *ctx)
121 {
122   srtp_ctx_free_policy (ctx);
123   if (CLIB_DEBUG)
124     clib_memset (ctx, 0xfb, sizeof (*ctx));
125   pool_put (srtp_main.listener_ctx_pool, ctx);
126 }
127
128 srtp_tc_t *
129 srtp_listener_ctx_get (u32 ctx_index)
130 {
131   return pool_elt_at_index (srtp_main.listener_ctx_pool, ctx_index);
132 }
133
134 u32
135 srtp_listener_ctx_index (srtp_tc_t *ctx)
136 {
137   return (ctx - srtp_main.listener_ctx_pool);
138 }
139
140 static int
141 srtp_ctx_init_client (srtp_tc_t *ctx)
142 {
143   session_t *app_session;
144   app_worker_t *app_wrk;
145   session_error_t err;
146
147   if (srtp_create (&ctx->srtp_ctx, &ctx->srtp_policy[0]) != srtp_err_status_ok)
148     {
149       SRTP_DBG (0, "failed to init srtp ctx");
150       return -1;
151     }
152
153   app_wrk = app_worker_get (ctx->parent_app_wrk_index);
154   app_session = session_get (ctx->c_s_index, ctx->c_thread_index);
155   app_session->app_wrk_index = ctx->parent_app_wrk_index;
156   app_session->connection_index = ctx->srtp_ctx_handle;
157   app_session->session_type =
158     session_type_from_proto_and_ip (TRANSPORT_PROTO_SRTP, ctx->udp_is_ip4);
159
160   if ((err = app_worker_init_connected (app_wrk, app_session)))
161     goto failed;
162
163   app_session->session_state = SESSION_STATE_READY;
164   if (app_worker_connect_notify (app_wrk, app_session, SESSION_E_NONE,
165                                  ctx->parent_app_api_context))
166     {
167       SRTP_DBG (0, "failed to notify app");
168       app_session->session_state = SESSION_STATE_CONNECTING;
169       srtp_disconnect (ctx->srtp_ctx_handle, vlib_get_thread_index ());
170       return -1;
171     }
172
173   ctx->app_session_handle = session_handle (app_session);
174
175   return 0;
176
177 failed:
178   /* Free app session pre-allocated when transport was established */
179   session_free (session_get (ctx->c_s_index, ctx->c_thread_index));
180   ctx->no_app_session = 1;
181   srtp_disconnect (ctx->srtp_ctx_handle, vlib_get_thread_index ());
182   return app_worker_connect_notify (app_wrk, 0, err,
183                                     ctx->parent_app_api_context);
184 }
185
186 static int
187 srtp_ctx_init_server (srtp_tc_t *ctx)
188 {
189   session_t *app_listener, *app_session;
190   app_worker_t *app_wrk;
191   srtp_tc_t *lctx;
192   int rv;
193
194   if (srtp_create (&ctx->srtp_ctx, ctx->srtp_policy) != srtp_err_status_ok)
195     return -1;
196
197   lctx = srtp_listener_ctx_get (ctx->listener_ctx_index);
198   app_listener = listen_session_get_from_handle (lctx->app_session_handle);
199
200   app_session = session_get (ctx->c_s_index, ctx->c_thread_index);
201   app_session->app_wrk_index = ctx->parent_app_wrk_index;
202   app_session->connection_index = ctx->srtp_ctx_handle;
203   app_session->session_type = app_listener->session_type;
204   app_session->listener_handle = listen_session_get_handle (app_listener);
205   app_session->session_state = SESSION_STATE_ACCEPTING;
206
207   if ((rv = app_worker_init_accepted (app_session)))
208     {
209       SRTP_DBG (1, "failed to allocate fifos");
210       session_free (app_session);
211       return rv;
212     }
213   ctx->app_session_handle = session_handle (app_session);
214   ctx->parent_app_wrk_index = app_session->app_wrk_index;
215   app_wrk = app_worker_get (app_session->app_wrk_index);
216   return app_worker_accept_notify (app_wrk, app_session);
217 }
218
219 static int
220 srtp_ctx_deinit (srtp_tc_t *ctx)
221 {
222   if (srtp_dealloc (ctx->srtp_ctx))
223     SRTP_DBG (0, "%u failed to cleanup srtp state", ctx->c_c_index);
224   return 0;
225 }
226
227 static inline int
228 srtp_ctx_write (srtp_tc_t *ctx, session_t *app_session,
229                 transport_send_params_t *sp)
230 {
231   u32 n_wrote = 0, to_deq, dgram_sz;
232   session_dgram_pre_hdr_t hdr;
233   app_session_transport_t at = {};
234   svm_msg_q_t *mq;
235   session_t *us;
236   u8 buf[2000];
237   int rv, len;
238
239   sp->max_burst_size = sp->max_burst_size * TRANSPORT_PACER_MIN_MSS;
240
241   us = session_get_from_handle (ctx->srtp_session_handle);
242   to_deq = svm_fifo_max_dequeue_cons (app_session->tx_fifo);
243   mq = session_main_get_vpp_event_queue (us->thread_index);
244   sp->bytes_dequeued = to_deq;
245
246   while (to_deq > 0)
247     {
248       /* Peeking only pre-header dgram because the session is connected */
249       rv = svm_fifo_peek (app_session->tx_fifo, 0, sizeof (hdr), (u8 *) &hdr);
250       ASSERT (rv == sizeof (hdr) && hdr.data_length < 2000);
251       ASSERT (to_deq >= hdr.data_length + SESSION_CONN_HDR_LEN);
252
253       dgram_sz = hdr.data_length + SESSION_CONN_HDR_LEN;
254       if (svm_fifo_max_enqueue_prod (us->tx_fifo) < dgram_sz + 1000)
255         {
256           svm_fifo_add_want_deq_ntf (us->tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF);
257           transport_connection_deschedule (&ctx->connection);
258           sp->flags |= TRANSPORT_SND_F_DESCHED;
259           goto done;
260         }
261
262       rv = svm_fifo_peek (app_session->tx_fifo, SESSION_CONN_HDR_LEN,
263                           hdr.data_length, buf);
264       ASSERT (rv == hdr.data_length);
265       svm_fifo_dequeue_drop (app_session->tx_fifo, dgram_sz);
266
267       len = rv;
268
269       rv = srtp_protect (ctx->srtp_ctx, buf, &len);
270       if (rv != srtp_err_status_ok)
271         {
272           SRTP_DBG (0, "failed to protect %u", rv);
273           return 0;
274         }
275
276       rv = app_send_dgram_raw (us->tx_fifo, &at, mq, (u8 *) buf, len,
277                                SESSION_IO_EVT_TX, 1 /* do_evt */,
278                                0 /* noblock */);
279       ASSERT (rv == len);
280
281       n_wrote += rv;
282       to_deq -= dgram_sz;
283     }
284
285 done:
286
287   if (svm_fifo_needs_deq_ntf (app_session->tx_fifo, n_wrote))
288     session_dequeue_notify (app_session);
289
290   if (n_wrote)
291     {
292       if (svm_fifo_set_event (us->tx_fifo))
293         session_send_io_evt_to_thread (us->tx_fifo, SESSION_IO_EVT_TX);
294     }
295
296   if (PREDICT_FALSE (ctx->app_closed &&
297                      !svm_fifo_max_enqueue_prod (us->rx_fifo)))
298     {
299       srtp_disconnect_transport (ctx);
300       session_transport_closed_notify (&ctx->connection);
301     }
302
303   ASSERT (sp->bytes_dequeued >= to_deq);
304   sp->bytes_dequeued -= to_deq;
305
306   return n_wrote > 0 ? clib_max (n_wrote / TRANSPORT_PACER_MIN_MSS, 1) : 0;
307 }
308
309 int
310 srtp_add_vpp_q_builtin_rx_evt (session_t *s)
311 {
312   session_enqueue_notify (s);
313   return 0;
314 }
315
316 void
317 srtp_notify_app_enqueue (srtp_tc_t *ctx, session_t *app_session)
318 {
319   app_worker_t *app_wrk;
320   app_wrk = app_worker_get_if_valid (app_session->app_wrk_index);
321   if (PREDICT_TRUE (app_wrk != 0))
322     app_worker_rx_notify (app_wrk, app_session);
323 }
324
325 static inline int
326 srtp_ctx_read (srtp_tc_t *ctx, session_t *us)
327 {
328   app_session_transport_t at;
329   session_dgram_hdr_t hdr;
330   session_t *app_session;
331   u32 n_read = 0;
332   u8 buf[2000];
333   int rv, len;
334
335   app_session = session_get_from_handle (ctx->app_session_handle);
336   svm_fifo_fill_chunk_list (app_session->rx_fifo);
337
338   while (svm_fifo_max_dequeue_cons (us->rx_fifo) > 0)
339     {
340       if (svm_fifo_max_enqueue_prod (app_session->rx_fifo) < 2000)
341         {
342           srtp_add_vpp_q_builtin_rx_evt (us);
343           goto done;
344         }
345
346       rv = app_recv_dgram_raw (us->rx_fifo, (u8 *) buf, 2000, &at,
347                                0 /* clear evt */, 0 /* peek */);
348       ASSERT (rv > 0);
349       len = rv;
350
351       rv = srtp_unprotect (ctx->srtp_ctx, buf, &len);
352
353       if (rv != srtp_err_status_ok)
354         {
355           SRTP_DBG (0, "failed to unprotect %d", rv);
356           return 0;
357         }
358       n_read += len;
359
360       hdr.data_length = len;
361       hdr.data_offset = 0;
362
363       svm_fifo_seg_t segs[2] = { { (u8 *) &hdr, sizeof (hdr) }, { buf, len } };
364
365       rv = svm_fifo_enqueue_segments (app_session->rx_fifo, segs, 2,
366                                       0 /* allow partial */);
367       ASSERT (rv > 0);
368     }
369
370 done:
371
372   srtp_notify_app_enqueue (ctx, app_session);
373
374   return n_read;
375 }
376
377 int
378 srtp_add_segment_callback (u32 client_index, u64 segment_handle)
379 {
380   /* No-op for builtin */
381   return 0;
382 }
383
384 int
385 srtp_del_segment_callback (u32 client_index, u64 segment_handle)
386 {
387   return 0;
388 }
389
390 void
391 srtp_session_disconnect_callback (session_t *us)
392 {
393   clib_warning ("udp %u disconnected?", us->session_index);
394 }
395
396 void
397 srtp_session_reset_callback (session_t *us)
398 {
399   clib_warning ("udp %u reset?", us->session_index);
400 }
401
402 static int
403 srtp_session_connected_callback (u32 srtp_app_index, u32 ctx_handle,
404                                  session_t *us, session_error_t err)
405 {
406   session_t *app_session;
407   session_type_t st;
408   srtp_tc_t *ctx;
409
410   ctx = srtp_ctx_get_w_thread (ctx_handle, 1 /* udp allocs on thread 1 */);
411
412   ctx->srtp_session_handle = session_handle (us);
413   ctx->c_flags |= TRANSPORT_CONNECTION_F_NO_LOOKUP;
414   us->opaque = ctx_handle;
415
416   /* Preallocate app session. Avoids allocating a session on srtp_session rx
417    * and potentially invalidating the session pool */
418   app_session = session_alloc (ctx->c_thread_index);
419   app_session->session_state = SESSION_STATE_CREATED;
420   ctx->c_s_index = app_session->session_index;
421
422   st = session_type_from_proto_and_ip (TRANSPORT_PROTO_SRTP, ctx->udp_is_ip4);
423   app_session->session_type = st;
424   app_session->connection_index = ctx->srtp_ctx_handle;
425
426   return srtp_ctx_init_client (ctx);
427 }
428
429 int
430 srtp_session_accept_callback (session_t *us)
431 {
432   session_t *srtp_listener, *app_session;
433   srtp_tc_t *lctx, *ctx;
434   u32 ctx_handle;
435
436   srtp_listener = listen_session_get_from_handle (us->listener_handle);
437   lctx = srtp_listener_ctx_get (srtp_listener->opaque);
438
439   ctx_handle = srtp_ctx_alloc_w_thread (us->thread_index);
440   ctx = srtp_ctx_get_w_thread (ctx_handle, us->thread_index);
441   clib_memcpy_fast (ctx, lctx, sizeof (*lctx));
442   ctx->c_thread_index = vlib_get_thread_index ();
443   ctx->srtp_ctx_handle = ctx_handle;
444   us->session_state = SESSION_STATE_READY;
445   us->opaque = ctx_handle;
446   ctx->srtp_session_handle = session_handle (us);
447   ctx->listener_ctx_index = srtp_listener->opaque;
448   ctx->c_flags |= TRANSPORT_CONNECTION_F_NO_LOOKUP;
449
450   ctx->srtp_policy[0].key = clib_mem_alloc (SRTP_MAX_KEYLEN);
451   clib_memcpy (ctx->srtp_policy[0].key, lctx->srtp_policy[0].key,
452                SRTP_MAX_KEYLEN);
453   ctx->srtp_policy[1].key = clib_mem_alloc (SRTP_MAX_KEYLEN);
454   clib_memcpy (ctx->srtp_policy[1].key, lctx->srtp_policy[1].key,
455                SRTP_MAX_KEYLEN);
456
457   app_session = session_alloc (ctx->c_thread_index);
458   app_session->session_state = SESSION_STATE_CREATED;
459   ctx->c_s_index = app_session->session_index;
460
461   SRTP_DBG (1, "Accept on listener %u new connection [%u]%x",
462             srtp_listener->opaque, vlib_get_thread_index (), ctx_handle);
463
464   return srtp_ctx_init_server (ctx);
465 }
466
467 int
468 srtp_app_rx_callback (session_t *us)
469 {
470   srtp_tc_t *ctx;
471
472   ctx = srtp_ctx_get_w_thread (us->opaque, us->thread_index);
473   srtp_ctx_read (ctx, us);
474   return 0;
475 }
476
477 int
478 srtp_app_tx_callback (session_t *us)
479 {
480   srtp_tc_t *ctx;
481
482   ctx = srtp_ctx_get_w_thread (us->opaque, us->thread_index);
483   transport_connection_reschedule (&ctx->connection);
484
485   return 0;
486 }
487
488 static void
489 srtp_app_session_cleanup (session_t *s, session_cleanup_ntf_t ntf)
490 {
491   srtp_tc_t *ctx;
492
493   if (ntf == SESSION_CLEANUP_TRANSPORT)
494     {
495       /* Allow cleanup of tcp session */
496       if (s->session_state == SESSION_STATE_TRANSPORT_DELETED)
497         session_close (s);
498       return;
499     }
500
501   ctx = srtp_ctx_get_w_thread (s->opaque, s->thread_index);
502   if (!ctx->no_app_session)
503     session_transport_delete_notify (&ctx->connection);
504   srtp_ctx_deinit (ctx);
505   srtp_ctx_free (ctx);
506 }
507
508 static void
509 srtp_migrate_ctx_reply (void *arg)
510 {
511   u32 ctx_index = pointer_to_uword (arg);
512   srtp_tc_t *ctx;
513
514   ctx = srtp_ctx_get_w_thread (ctx_index, vlib_get_thread_index ());
515   srtp_ctx_free (ctx);
516 }
517
518 static void
519 srtp_migrate_ctx (void *arg)
520 {
521   u32 ctx_handle, thread_index, old_thread_index, old_ctx_index;
522   srtp_tc_t *ctx = (srtp_tc_t *) arg;
523   session_t *us, *new_app_session;
524   void *rargs;
525
526   old_thread_index = ctx->c_thread_index;
527   old_ctx_index = ctx->c_c_index;
528   thread_index = session_thread_from_handle (ctx->srtp_session_handle);
529   ASSERT (thread_index == vlib_get_thread_index ());
530
531   ctx_handle = srtp_ctx_attach (thread_index, ctx);
532   ctx = srtp_ctx_get_w_thread (ctx_handle, thread_index);
533   ctx->srtp_ctx_handle = ctx_handle;
534   SRTP_DBG (1, "migrated ctx handle %u", ctx_handle);
535
536   us = session_get_from_handle (ctx->srtp_session_handle);
537   us->opaque = ctx_handle;
538   us->flags &= ~SESSION_F_IS_MIGRATING;
539   if (svm_fifo_max_dequeue (us->tx_fifo))
540     session_send_io_evt_to_thread (us->tx_fifo, SESSION_IO_EVT_TX);
541
542   /* Migrate app session as well */
543   session_dgram_connect_notify (&ctx->connection, old_thread_index,
544                                 &new_app_session);
545
546   /* Call back original thread and ask for cleanup */
547   rargs = uword_to_pointer ((uword) old_ctx_index, void *);
548   session_send_rpc_evt_to_thread (old_thread_index, srtp_migrate_ctx_reply,
549                                   rargs);
550 }
551
552 static void
553 srtp_session_migrate_callback (session_t *us, session_handle_t new_sh)
554 {
555   u32 new_thread = session_thread_from_handle (new_sh);
556   srtp_tc_t *ctx, *cloned_ctx;
557
558   ctx = srtp_ctx_get_w_thread (us->opaque, us->thread_index);
559   ctx->srtp_session_handle = new_sh;
560   cloned_ctx = srtp_ctx_detach (ctx);
561   SRTP_DBG (1, "ctx %u attached to udp %x session migrating",
562             cloned_ctx->c_c_index, new_sh);
563
564   session_send_rpc_evt_to_thread (new_thread, srtp_migrate_ctx,
565                                   (void *) cloned_ctx);
566
567   /* Can't free ctx now because app might be sending as srtp is not
568    * connection oriented, so it won't wait for a handshake */
569   ctx->is_migrated = 1;
570 }
571
572 static session_cb_vft_t srtp_app_cb_vft = {
573   .session_accept_callback = srtp_session_accept_callback,
574   .session_disconnect_callback = srtp_session_disconnect_callback,
575   .session_connected_callback = srtp_session_connected_callback,
576   .session_reset_callback = srtp_session_reset_callback,
577   .add_segment_callback = srtp_add_segment_callback,
578   .del_segment_callback = srtp_del_segment_callback,
579   .builtin_app_rx_callback = srtp_app_rx_callback,
580   .builtin_app_tx_callback = srtp_app_tx_callback,
581   .session_migrate_callback = srtp_session_migrate_callback,
582   .session_cleanup_callback = srtp_app_session_cleanup,
583 };
584
585 static clib_error_t *
586 srtp_enable (vlib_main_t *vm, u8 is_en)
587 {
588   u32 add_segment_size = 256 << 20, first_seg_size = 32 << 20;
589   vnet_app_detach_args_t _da, *da = &_da;
590   vnet_app_attach_args_t _a, *a = &_a;
591   u64 options[APP_OPTIONS_N_OPTIONS];
592   srtp_main_t *sm = &srtp_main;
593   u32 fifo_size = 128 << 12;
594
595   if (!is_en)
596     {
597       da->app_index = sm->app_index;
598       da->api_client_index = APP_INVALID_INDEX;
599       vnet_application_detach (da);
600       return 0;
601     }
602
603   srtp_init ();
604   vec_validate (sm->ctx_pool, vlib_num_workers ());
605
606   first_seg_size = sm->first_seg_size ? sm->first_seg_size : first_seg_size;
607   fifo_size = sm->fifo_size ? sm->fifo_size : fifo_size;
608
609   clib_memset (a, 0, sizeof (*a));
610   clib_memset (options, 0, sizeof (options));
611
612   a->session_cb_vft = &srtp_app_cb_vft;
613   a->api_client_index = APP_INVALID_INDEX;
614   a->options = options;
615   a->name = format (0, "srtp");
616   a->options[APP_OPTIONS_SEGMENT_SIZE] = first_seg_size;
617   a->options[APP_OPTIONS_ADD_SEGMENT_SIZE] = add_segment_size;
618   a->options[APP_OPTIONS_RX_FIFO_SIZE] = fifo_size;
619   a->options[APP_OPTIONS_TX_FIFO_SIZE] = fifo_size;
620   a->options[APP_OPTIONS_FLAGS] = APP_OPTIONS_FLAGS_IS_BUILTIN;
621   a->options[APP_OPTIONS_FLAGS] |= APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE;
622   a->options[APP_OPTIONS_FLAGS] |= APP_OPTIONS_FLAGS_IS_TRANSPORT_APP;
623
624   if (vnet_application_attach (a))
625     return clib_error_return (0, "failed to attach srtp app");
626
627   sm->app_index = a->app_index;
628   vec_free (a->name);
629
630   return 0;
631 }
632
633 int
634 srtp_connect (transport_endpoint_cfg_t *tep)
635 {
636   vnet_connect_args_t _cargs = { {}, }, *cargs = &_cargs;
637   session_endpoint_cfg_t *sep;
638   srtp_main_t *sm = &srtp_main;
639   app_worker_t *app_wrk;
640   application_t *app;
641   srtp_tc_t *ctx;
642   u32 ctx_index;
643   int rv;
644
645   sep = (session_endpoint_cfg_t *) tep;
646   if (!sep->ext_cfg)
647     return SESSION_E_NOEXTCFG;
648
649   app_wrk = app_worker_get (sep->app_wrk_index);
650   app = application_get (app_wrk->app_index);
651
652   ctx_index = srtp_ctx_alloc_w_thread (1 /* because of udp */);
653   ctx = srtp_ctx_get_w_thread (ctx_index, 1);
654   ctx->parent_app_wrk_index = sep->app_wrk_index;
655   ctx->parent_app_api_context = sep->opaque;
656   ctx->udp_is_ip4 = sep->is_ip4;
657   ctx->srtp_ctx_handle = ctx_index;
658   ctx->c_flags |= TRANSPORT_CONNECTION_F_NO_LOOKUP;
659
660   srtp_init_policy (ctx, (transport_endpt_cfg_srtp_t *) sep->ext_cfg->data);
661
662   clib_memcpy_fast (&cargs->sep, sep, sizeof (session_endpoint_t));
663   cargs->sep.transport_proto = TRANSPORT_PROTO_UDP;
664   cargs->sep_ext.transport_flags = TRANSPORT_CFG_F_CONNECTED;
665   cargs->app_index = sm->app_index;
666   cargs->api_context = ctx_index;
667   cargs->sep_ext.ns_index = app->ns_index;
668   if ((rv = vnet_connect (cargs)))
669     return rv;
670
671   SRTP_DBG (1, "New connect request %u", ctx_index);
672   return 0;
673 }
674
675 static void
676 srtp_disconnect_transport (srtp_tc_t *ctx)
677 {
678   vnet_disconnect_args_t a = {
679     .handle = ctx->srtp_session_handle,
680     .app_index = srtp_main.app_index,
681   };
682
683   if (vnet_disconnect_session (&a))
684     SRTP_DBG (0, "disconnect returned");
685 }
686
687 static void
688 srtp_disconnect (u32 ctx_handle, u32 thread_index)
689 {
690   session_t *app_session;
691   srtp_tc_t *ctx;
692
693   SRTP_DBG (1, "App disconnecting %x", ctx_handle);
694
695   ctx = srtp_ctx_get_w_thread (ctx_handle, thread_index);
696
697   app_session = session_get_from_handle (ctx->app_session_handle);
698   if (!svm_fifo_max_dequeue_cons (app_session->tx_fifo))
699     {
700       /* Confirm close */
701       srtp_disconnect_transport (ctx);
702       session_transport_closed_notify (&ctx->connection);
703     }
704   else
705     {
706       /* Wait for all data to be written to udp */
707       ctx->app_closed = 1;
708     }
709 }
710
711 static u32
712 srtp_start_listen (u32 app_listener_index, transport_endpoint_cfg_t *tep)
713 {
714   vnet_listen_args_t _bargs, *args = &_bargs;
715   session_handle_t udp_al_handle;
716   srtp_main_t *sm = &srtp_main;
717   session_endpoint_cfg_t *sep;
718   session_t *srtp_listener;
719   session_t *app_listener;
720   app_worker_t *app_wrk;
721   application_t *app;
722   app_listener_t *al;
723   srtp_tc_t *lctx;
724   u32 lctx_index;
725
726   sep = (session_endpoint_cfg_t *) tep;
727   if (!sep->ext_cfg)
728     return SESSION_E_NOEXTCFG;
729
730   app_wrk = app_worker_get (sep->app_wrk_index);
731   app = application_get (app_wrk->app_index);
732
733   clib_memset (args, 0, sizeof (*args));
734   args->app_index = sm->app_index;
735   args->sep_ext = *sep;
736   args->sep_ext.ns_index = app->ns_index;
737   args->sep_ext.transport_proto = TRANSPORT_PROTO_UDP;
738   args->sep_ext.transport_flags = TRANSPORT_CFG_F_CONNECTED;
739   if (vnet_listen (args))
740     return -1;
741
742   lctx_index = srtp_listener_ctx_alloc ();
743   udp_al_handle = args->handle;
744   al = app_listener_get_w_handle (udp_al_handle);
745   srtp_listener = app_listener_get_session (al);
746   srtp_listener->opaque = lctx_index;
747
748   app_listener = listen_session_get (app_listener_index);
749
750   lctx = srtp_listener_ctx_get (lctx_index);
751   lctx->parent_app_wrk_index = sep->app_wrk_index;
752   lctx->srtp_session_handle = udp_al_handle;
753   lctx->app_session_handle = listen_session_get_handle (app_listener);
754   lctx->udp_is_ip4 = sep->is_ip4;
755   lctx->c_s_index = app_listener_index;
756   lctx->c_flags |= TRANSPORT_CONNECTION_F_NO_LOOKUP;
757
758   srtp_init_policy (lctx, (transport_endpt_cfg_srtp_t *) sep->ext_cfg->data);
759
760   SRTP_DBG (1, "Started listening %d", lctx_index);
761   return lctx_index;
762 }
763
764 u32
765 srtp_stop_listen (u32 lctx_index)
766 {
767   session_endpoint_t sep = SESSION_ENDPOINT_NULL;
768   transport_connection_t *lc;
769   srtp_tc_t *lctx;
770   session_t *ls;
771   int rv;
772
773   lctx = srtp_listener_ctx_get (lctx_index);
774
775   /* Cleanup listener from session lookup table */
776   ls = session_get_from_handle (lctx->srtp_session_handle);
777   lc = session_get_transport (ls);
778
779   sep.fib_index = lc->fib_index;
780   sep.port = lc->lcl_port;
781   sep.is_ip4 = lc->is_ip4;
782   sep.transport_proto = TRANSPORT_PROTO_SRTP;
783   clib_memcpy (&sep.ip, &lc->lcl_ip, sizeof (lc->lcl_ip));
784   session_lookup_del_session_endpoint2 (&sep);
785
786   vnet_unlisten_args_t a = {
787     .handle = lctx->srtp_session_handle,
788     .app_index = srtp_main.app_index,
789     .wrk_map_index = 0 /* default wrk */
790   };
791   if ((rv = vnet_unlisten (&a)))
792     SRTP_DBG (0, "unlisten returned %d", rv);
793
794   srtp_listener_ctx_free (lctx);
795   return 0;
796 }
797
798 transport_connection_t *
799 srtp_connection_get (u32 ctx_index, u32 thread_index)
800 {
801   srtp_tc_t *ctx;
802   ctx = srtp_ctx_get_w_thread (ctx_index, thread_index);
803   return &ctx->connection;
804 }
805
806 transport_connection_t *
807 srtp_listener_get (u32 listener_index)
808 {
809   srtp_tc_t *ctx;
810   ctx = srtp_listener_ctx_get (listener_index);
811   return &ctx->connection;
812 }
813
814 int
815 srtp_custom_tx_callback (void *session, transport_send_params_t *sp)
816 {
817   session_t *app_session = (session_t *) session;
818   srtp_tc_t *ctx;
819
820   if (PREDICT_FALSE (app_session->session_state >=
821                      SESSION_STATE_TRANSPORT_CLOSED))
822     return 0;
823
824   ctx = srtp_ctx_get_w_thread (app_session->connection_index,
825                                app_session->thread_index);
826   if (PREDICT_FALSE (ctx->is_migrated))
827     return 0;
828
829   return srtp_ctx_write (ctx, app_session, sp);
830 }
831
832 u8 *
833 format_srtp_ctx (u8 *s, va_list *args)
834 {
835   srtp_tc_t *ctx = va_arg (*args, srtp_tc_t *);
836   u32 udp_si, udp_ti;
837
838   session_parse_handle (ctx->srtp_session_handle, &udp_si, &udp_ti);
839   s = format (s, "[%d:%d][SRTP] app_wrk %u index %u udp %d:%d",
840               ctx->c_thread_index, ctx->c_s_index, ctx->parent_app_wrk_index,
841               ctx->srtp_ctx_handle, udp_ti, udp_si);
842
843   return s;
844 }
845
846 static u8 *
847 format_srtp_listener_ctx (u8 *s, va_list *args)
848 {
849   session_t *udp_listener;
850   app_listener_t *al;
851   srtp_tc_t *ctx;
852
853   ctx = va_arg (*args, srtp_tc_t *);
854
855   al = app_listener_get_w_handle (ctx->srtp_session_handle);
856   udp_listener = app_listener_get_session (al);
857   s = format (s, "[%d:%d][SRTP] app_wrk %u udp %d:%d", ctx->c_thread_index,
858               ctx->c_s_index, ctx->parent_app_wrk_index,
859               udp_listener->thread_index, udp_listener->session_index);
860
861   return s;
862 }
863
864 static u8 *
865 format_srtp_ctx_state (u8 *s, va_list *args)
866 {
867   srtp_tc_t *ctx;
868   session_t *us;
869
870   ctx = va_arg (*args, srtp_tc_t *);
871   us = session_get (ctx->c_s_index, ctx->c_thread_index);
872   if (us->session_state == SESSION_STATE_LISTENING)
873     s = format (s, "%s", "LISTEN");
874   else
875     {
876       if (us->session_state >= SESSION_STATE_TRANSPORT_CLOSED)
877         s = format (s, "%s", "CLOSED");
878       else if (us->session_state == SESSION_STATE_APP_CLOSED)
879         s = format (s, "%s", "APP-CLOSED");
880       else if (us->session_state >= SESSION_STATE_TRANSPORT_CLOSING)
881         s = format (s, "%s", "CLOSING");
882       else
883         s = format (s, "%s", "ESTABLISHED");
884     }
885
886   return s;
887 }
888
889 u8 *
890 format_srtp_connection (u8 *s, va_list *args)
891 {
892   u32 ctx_index = va_arg (*args, u32);
893   u32 thread_index = va_arg (*args, u32);
894   u32 verbose = va_arg (*args, u32);
895   srtp_tc_t *ctx;
896
897   ctx = srtp_ctx_get_w_thread (ctx_index, thread_index);
898   if (!ctx)
899     return s;
900
901   s = format (s, "%-" SESSION_CLI_ID_LEN "U", format_srtp_ctx, ctx);
902   if (verbose)
903     {
904       s =
905         format (s, "%-" SESSION_CLI_STATE_LEN "U", format_srtp_ctx_state, ctx);
906       if (verbose > 1)
907         s = format (s, "\n");
908     }
909   return s;
910 }
911
912 u8 *
913 format_srtp_listener (u8 *s, va_list *args)
914 {
915   u32 tc_index = va_arg (*args, u32);
916   u32 __clib_unused thread_index = va_arg (*args, u32);
917   u32 verbose = va_arg (*args, u32);
918   srtp_tc_t *ctx = srtp_listener_ctx_get (tc_index);
919
920   s = format (s, "%-" SESSION_CLI_ID_LEN "U", format_srtp_listener_ctx, ctx);
921   if (verbose)
922     s = format (s, "%-" SESSION_CLI_STATE_LEN "U", format_srtp_ctx_state, ctx);
923   return s;
924 }
925
926 u8 *
927 format_srtp_half_open (u8 *s, va_list *args)
928 {
929   return 0;
930 }
931
932 static void
933 srtp_transport_endpoint_get (u32 ctx_handle, u32 thread_index,
934                              transport_endpoint_t *tep, u8 is_lcl)
935 {
936   srtp_tc_t *ctx = srtp_ctx_get_w_thread (ctx_handle, thread_index);
937   session_t *udp_session;
938
939   udp_session = session_get_from_handle (ctx->srtp_session_handle);
940   session_get_endpoint (udp_session, tep, is_lcl);
941 }
942
943 static void
944 srtp_transport_listener_endpoint_get (u32 ctx_handle,
945                                       transport_endpoint_t *tep, u8 is_lcl)
946 {
947   session_t *srtp_listener;
948   app_listener_t *al;
949   srtp_tc_t *ctx = srtp_listener_ctx_get (ctx_handle);
950
951   al = app_listener_get_w_handle (ctx->srtp_session_handle);
952   srtp_listener = app_listener_get_session (al);
953   session_get_endpoint (srtp_listener, tep, is_lcl);
954 }
955
956 static const transport_proto_vft_t srtp_proto = {
957   .enable = srtp_enable,
958   .connect = srtp_connect,
959   .close = srtp_disconnect,
960   .start_listen = srtp_start_listen,
961   .stop_listen = srtp_stop_listen,
962   .get_connection = srtp_connection_get,
963   .get_listener = srtp_listener_get,
964   .custom_tx = srtp_custom_tx_callback,
965   .format_connection = format_srtp_connection,
966   .format_half_open = format_srtp_half_open,
967   .format_listener = format_srtp_listener,
968   .get_transport_endpoint = srtp_transport_endpoint_get,
969   .get_transport_listener_endpoint = srtp_transport_listener_endpoint_get,
970   .transport_options = {
971     .name = "srtp",
972     .short_name = "R",
973     .tx_type = TRANSPORT_TX_INTERNAL,
974     .service_type = TRANSPORT_SERVICE_APP,
975   },
976 };
977
978 static clib_error_t *
979 srtp_transport_init (vlib_main_t *vm)
980 {
981   transport_register_protocol (TRANSPORT_PROTO_SRTP, &srtp_proto,
982                                FIB_PROTOCOL_IP4, ~0);
983   transport_register_protocol (TRANSPORT_PROTO_SRTP, &srtp_proto,
984                                FIB_PROTOCOL_IP6, ~0);
985   return 0;
986 }
987
988 VLIB_INIT_FUNCTION (srtp_transport_init);
989
990 VLIB_PLUGIN_REGISTER () = {
991   .version = VPP_BUILD_VER,
992   .description = "Secure Real-time Transport Protocol (SRTP)",
993   .default_disabled = 1,
994 };
995
996 /*
997  * fd.io coding-style-patch-verification: ON
998  *
999  * Local Variables:
1000  * eval: (c-set-style "gnu")
1001  * End:
1002  */