srtp: fix build on ubuntu-22.04
[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   if (svm_fifo_set_event (s->rx_fifo))
313     session_send_io_evt_to_thread (s->rx_fifo, SESSION_IO_EVT_BUILTIN_RX);
314   return 0;
315 }
316
317 void
318 srtp_notify_app_enqueue (srtp_tc_t *ctx, session_t *app_session)
319 {
320   app_worker_t *app_wrk;
321   app_wrk = app_worker_get_if_valid (app_session->app_wrk_index);
322   if (PREDICT_TRUE (app_wrk != 0))
323     app_worker_lock_and_send_event (app_wrk, app_session, SESSION_IO_EVT_RX);
324 }
325
326 static inline int
327 srtp_ctx_read (srtp_tc_t *ctx, session_t *us)
328 {
329   app_session_transport_t at;
330   session_dgram_hdr_t hdr;
331   session_t *app_session;
332   u32 n_read = 0;
333   u8 buf[2000];
334   int rv, len;
335
336   app_session = session_get_from_handle (ctx->app_session_handle);
337   svm_fifo_fill_chunk_list (app_session->rx_fifo);
338
339   while (svm_fifo_max_dequeue_cons (us->rx_fifo) > 0)
340     {
341       if (svm_fifo_max_enqueue_prod (app_session->rx_fifo) < 2000)
342         {
343           srtp_add_vpp_q_builtin_rx_evt (us);
344           goto done;
345         }
346
347       rv = app_recv_dgram_raw (us->rx_fifo, (u8 *) buf, 2000, &at,
348                                0 /* clear evt */, 0 /* peek */);
349       ASSERT (rv > 0);
350       len = rv;
351
352       rv = srtp_unprotect (ctx->srtp_ctx, buf, &len);
353
354       if (rv != srtp_err_status_ok)
355         {
356           SRTP_DBG (0, "failed to unprotect %d", rv);
357           return 0;
358         }
359       n_read += len;
360
361       hdr.data_length = len;
362       hdr.data_offset = 0;
363
364       svm_fifo_seg_t segs[2] = { { (u8 *) &hdr, sizeof (hdr) }, { buf, len } };
365
366       rv = svm_fifo_enqueue_segments (app_session->rx_fifo, segs, 2,
367                                       0 /* allow partial */);
368       ASSERT (rv > 0);
369     }
370
371 done:
372
373   srtp_notify_app_enqueue (ctx, app_session);
374
375   return n_read;
376 }
377
378 int
379 srtp_add_segment_callback (u32 client_index, u64 segment_handle)
380 {
381   /* No-op for builtin */
382   return 0;
383 }
384
385 int
386 srtp_del_segment_callback (u32 client_index, u64 segment_handle)
387 {
388   return 0;
389 }
390
391 void
392 srtp_session_disconnect_callback (session_t *us)
393 {
394   clib_warning ("udp %u disconnected?", us->session_index);
395 }
396
397 void
398 srtp_session_reset_callback (session_t *us)
399 {
400   clib_warning ("udp %u reset?", us->session_index);
401 }
402
403 static int
404 srtp_session_connected_callback (u32 srtp_app_index, u32 ctx_handle,
405                                  session_t *us, session_error_t err)
406 {
407   session_t *app_session;
408   session_type_t st;
409   srtp_tc_t *ctx;
410
411   ctx = srtp_ctx_get_w_thread (ctx_handle, 1 /* udp allocs on thread 1 */);
412
413   ctx->srtp_session_handle = session_handle (us);
414   ctx->c_flags |= TRANSPORT_CONNECTION_F_NO_LOOKUP;
415   us->opaque = ctx_handle;
416
417   /* Preallocate app session. Avoids allocating a session on srtp_session rx
418    * and potentially invalidating the session pool */
419   app_session = session_alloc (ctx->c_thread_index);
420   app_session->session_state = SESSION_STATE_CREATED;
421   ctx->c_s_index = app_session->session_index;
422
423   st = session_type_from_proto_and_ip (TRANSPORT_PROTO_SRTP, ctx->udp_is_ip4);
424   app_session->session_type = st;
425   app_session->connection_index = ctx->srtp_ctx_handle;
426
427   return srtp_ctx_init_client (ctx);
428 }
429
430 int
431 srtp_session_accept_callback (session_t *us)
432 {
433   session_t *srtp_listener, *app_session;
434   srtp_tc_t *lctx, *ctx;
435   u32 ctx_handle;
436
437   srtp_listener = listen_session_get_from_handle (us->listener_handle);
438   lctx = srtp_listener_ctx_get (srtp_listener->opaque);
439
440   ctx_handle = srtp_ctx_alloc_w_thread (us->thread_index);
441   ctx = srtp_ctx_get_w_thread (ctx_handle, us->thread_index);
442   clib_memcpy_fast (ctx, lctx, sizeof (*lctx));
443   ctx->c_thread_index = vlib_get_thread_index ();
444   ctx->srtp_ctx_handle = ctx_handle;
445   us->session_state = SESSION_STATE_READY;
446   us->opaque = ctx_handle;
447   ctx->srtp_session_handle = session_handle (us);
448   ctx->listener_ctx_index = srtp_listener->opaque;
449   ctx->c_flags |= TRANSPORT_CONNECTION_F_NO_LOOKUP;
450
451   ctx->srtp_policy[0].key = clib_mem_alloc (SRTP_MAX_KEYLEN);
452   clib_memcpy (ctx->srtp_policy[0].key, lctx->srtp_policy[0].key,
453                SRTP_MAX_KEYLEN);
454   ctx->srtp_policy[1].key = clib_mem_alloc (SRTP_MAX_KEYLEN);
455   clib_memcpy (ctx->srtp_policy[1].key, lctx->srtp_policy[1].key,
456                SRTP_MAX_KEYLEN);
457
458   app_session = session_alloc (ctx->c_thread_index);
459   app_session->session_state = SESSION_STATE_CREATED;
460   ctx->c_s_index = app_session->session_index;
461
462   SRTP_DBG (1, "Accept on listener %u new connection [%u]%x",
463             srtp_listener->opaque, vlib_get_thread_index (), ctx_handle);
464
465   return srtp_ctx_init_server (ctx);
466 }
467
468 int
469 srtp_app_rx_callback (session_t *us)
470 {
471   srtp_tc_t *ctx;
472
473   ctx = srtp_ctx_get_w_thread (us->opaque, us->thread_index);
474   srtp_ctx_read (ctx, us);
475   return 0;
476 }
477
478 int
479 srtp_app_tx_callback (session_t *us)
480 {
481   srtp_tc_t *ctx;
482
483   ctx = srtp_ctx_get_w_thread (us->opaque, us->thread_index);
484   transport_connection_reschedule (&ctx->connection);
485
486   return 0;
487 }
488
489 static void
490 srtp_app_session_cleanup (session_t *s, session_cleanup_ntf_t ntf)
491 {
492   srtp_tc_t *ctx;
493
494   if (ntf == SESSION_CLEANUP_TRANSPORT)
495     {
496       /* Allow cleanup of tcp session */
497       if (s->session_state == SESSION_STATE_TRANSPORT_DELETED)
498         session_close (s);
499       return;
500     }
501
502   ctx = srtp_ctx_get_w_thread (s->opaque, s->thread_index);
503   if (!ctx->no_app_session)
504     session_transport_delete_notify (&ctx->connection);
505   srtp_ctx_deinit (ctx);
506   srtp_ctx_free (ctx);
507 }
508
509 static void
510 srtp_migrate_ctx_reply (void *arg)
511 {
512   u32 ctx_index = pointer_to_uword (arg);
513   srtp_tc_t *ctx;
514
515   ctx = srtp_ctx_get_w_thread (ctx_index, vlib_get_thread_index ());
516   srtp_ctx_free (ctx);
517 }
518
519 static void
520 srtp_migrate_ctx (void *arg)
521 {
522   u32 ctx_handle, thread_index, old_thread_index, old_ctx_index;
523   srtp_tc_t *ctx = (srtp_tc_t *) arg;
524   session_t *us, *new_app_session;
525   void *rargs;
526
527   old_thread_index = ctx->c_thread_index;
528   old_ctx_index = ctx->c_c_index;
529   thread_index = session_thread_from_handle (ctx->srtp_session_handle);
530   ASSERT (thread_index == vlib_get_thread_index ());
531
532   ctx_handle = srtp_ctx_attach (thread_index, ctx);
533   ctx = srtp_ctx_get_w_thread (ctx_handle, thread_index);
534   ctx->srtp_ctx_handle = ctx_handle;
535   SRTP_DBG (1, "migrated ctx handle %u", ctx_handle);
536
537   us = session_get_from_handle (ctx->srtp_session_handle);
538   us->opaque = ctx_handle;
539   us->flags &= ~SESSION_F_IS_MIGRATING;
540   if (svm_fifo_max_dequeue (us->tx_fifo))
541     session_send_io_evt_to_thread (us->tx_fifo, SESSION_IO_EVT_TX);
542
543   /* Migrate app session as well */
544   session_dgram_connect_notify (&ctx->connection, old_thread_index,
545                                 &new_app_session);
546
547   /* Call back original thread and ask for cleanup */
548   rargs = uword_to_pointer ((uword) old_ctx_index, void *);
549   session_send_rpc_evt_to_thread (old_thread_index, srtp_migrate_ctx_reply,
550                                   rargs);
551 }
552
553 static void
554 srtp_session_migrate_callback (session_t *us, session_handle_t new_sh)
555 {
556   u32 new_thread = session_thread_from_handle (new_sh);
557   srtp_tc_t *ctx, *cloned_ctx;
558
559   ctx = srtp_ctx_get_w_thread (us->opaque, us->thread_index);
560   ctx->srtp_session_handle = new_sh;
561   cloned_ctx = srtp_ctx_detach (ctx);
562   SRTP_DBG (1, "ctx %u attached to udp %x session migrating",
563             cloned_ctx->c_c_index, new_sh);
564
565   session_send_rpc_evt_to_thread (new_thread, srtp_migrate_ctx,
566                                   (void *) cloned_ctx);
567
568   /* Can't free ctx now because app might be sending as srtp is not
569    * connection oriented, so it won't wait for a handshake */
570   ctx->is_migrated = 1;
571 }
572
573 static session_cb_vft_t srtp_app_cb_vft = {
574   .session_accept_callback = srtp_session_accept_callback,
575   .session_disconnect_callback = srtp_session_disconnect_callback,
576   .session_connected_callback = srtp_session_connected_callback,
577   .session_reset_callback = srtp_session_reset_callback,
578   .add_segment_callback = srtp_add_segment_callback,
579   .del_segment_callback = srtp_del_segment_callback,
580   .builtin_app_rx_callback = srtp_app_rx_callback,
581   .builtin_app_tx_callback = srtp_app_tx_callback,
582   .session_migrate_callback = srtp_session_migrate_callback,
583   .session_cleanup_callback = srtp_app_session_cleanup,
584 };
585
586 static clib_error_t *
587 srtp_enable (vlib_main_t *vm, u8 is_en)
588 {
589   u32 add_segment_size = 256 << 20, first_seg_size = 32 << 20;
590   vnet_app_detach_args_t _da, *da = &_da;
591   vnet_app_attach_args_t _a, *a = &_a;
592   u64 options[APP_OPTIONS_N_OPTIONS];
593   srtp_main_t *sm = &srtp_main;
594   u32 fifo_size = 128 << 12;
595
596   if (!is_en)
597     {
598       da->app_index = sm->app_index;
599       da->api_client_index = APP_INVALID_INDEX;
600       vnet_application_detach (da);
601       return 0;
602     }
603
604   srtp_init ();
605   vec_validate (sm->ctx_pool, vlib_num_workers ());
606
607   first_seg_size = sm->first_seg_size ? sm->first_seg_size : first_seg_size;
608   fifo_size = sm->fifo_size ? sm->fifo_size : fifo_size;
609
610   clib_memset (a, 0, sizeof (*a));
611   clib_memset (options, 0, sizeof (options));
612
613   a->session_cb_vft = &srtp_app_cb_vft;
614   a->api_client_index = APP_INVALID_INDEX;
615   a->options = options;
616   a->name = format (0, "srtp");
617   a->options[APP_OPTIONS_SEGMENT_SIZE] = first_seg_size;
618   a->options[APP_OPTIONS_ADD_SEGMENT_SIZE] = add_segment_size;
619   a->options[APP_OPTIONS_RX_FIFO_SIZE] = fifo_size;
620   a->options[APP_OPTIONS_TX_FIFO_SIZE] = fifo_size;
621   a->options[APP_OPTIONS_FLAGS] = APP_OPTIONS_FLAGS_IS_BUILTIN;
622   a->options[APP_OPTIONS_FLAGS] |= APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE;
623   a->options[APP_OPTIONS_FLAGS] |= APP_OPTIONS_FLAGS_IS_TRANSPORT_APP;
624
625   if (vnet_application_attach (a))
626     return clib_error_return (0, "failed to attach srtp app");
627
628   sm->app_index = a->app_index;
629   vec_free (a->name);
630
631   return 0;
632 }
633
634 int
635 srtp_connect (transport_endpoint_cfg_t *tep)
636 {
637   vnet_connect_args_t _cargs = { {}, }, *cargs = &_cargs;
638   session_endpoint_cfg_t *sep;
639   srtp_main_t *sm = &srtp_main;
640   app_worker_t *app_wrk;
641   application_t *app;
642   srtp_tc_t *ctx;
643   u32 ctx_index;
644   int rv;
645
646   sep = (session_endpoint_cfg_t *) tep;
647   if (!sep->ext_cfg)
648     return SESSION_E_NOEXTCFG;
649
650   app_wrk = app_worker_get (sep->app_wrk_index);
651   app = application_get (app_wrk->app_index);
652
653   ctx_index = srtp_ctx_alloc_w_thread (1 /* because of udp */);
654   ctx = srtp_ctx_get_w_thread (ctx_index, 1);
655   ctx->parent_app_wrk_index = sep->app_wrk_index;
656   ctx->parent_app_api_context = sep->opaque;
657   ctx->udp_is_ip4 = sep->is_ip4;
658   ctx->srtp_ctx_handle = ctx_index;
659   ctx->c_flags |= TRANSPORT_CONNECTION_F_NO_LOOKUP;
660
661   srtp_init_policy (ctx, (transport_endpt_cfg_srtp_t *) sep->ext_cfg->data);
662
663   clib_memcpy_fast (&cargs->sep, sep, sizeof (session_endpoint_t));
664   cargs->sep.transport_proto = TRANSPORT_PROTO_UDP;
665   cargs->sep_ext.transport_flags = TRANSPORT_CFG_F_CONNECTED;
666   cargs->app_index = sm->app_index;
667   cargs->api_context = ctx_index;
668   cargs->sep_ext.ns_index = app->ns_index;
669   if ((rv = vnet_connect (cargs)))
670     return rv;
671
672   SRTP_DBG (1, "New connect request %u", ctx_index);
673   return 0;
674 }
675
676 static void
677 srtp_disconnect_transport (srtp_tc_t *ctx)
678 {
679   vnet_disconnect_args_t a = {
680     .handle = ctx->srtp_session_handle,
681     .app_index = srtp_main.app_index,
682   };
683
684   if (vnet_disconnect_session (&a))
685     SRTP_DBG (0, "disconnect returned");
686 }
687
688 static void
689 srtp_disconnect (u32 ctx_handle, u32 thread_index)
690 {
691   session_t *app_session;
692   srtp_tc_t *ctx;
693
694   SRTP_DBG (1, "App disconnecting %x", ctx_handle);
695
696   ctx = srtp_ctx_get_w_thread (ctx_handle, thread_index);
697
698   app_session = session_get_from_handle (ctx->app_session_handle);
699   if (!svm_fifo_max_dequeue_cons (app_session->tx_fifo))
700     {
701       /* Confirm close */
702       srtp_disconnect_transport (ctx);
703       session_transport_closed_notify (&ctx->connection);
704     }
705   else
706     {
707       /* Wait for all data to be written to udp */
708       ctx->app_closed = 1;
709     }
710 }
711
712 static u32
713 srtp_start_listen (u32 app_listener_index, transport_endpoint_cfg_t *tep)
714 {
715   vnet_listen_args_t _bargs, *args = &_bargs;
716   session_handle_t udp_al_handle;
717   srtp_main_t *sm = &srtp_main;
718   session_endpoint_cfg_t *sep;
719   session_t *srtp_listener;
720   session_t *app_listener;
721   app_worker_t *app_wrk;
722   application_t *app;
723   app_listener_t *al;
724   srtp_tc_t *lctx;
725   u32 lctx_index;
726
727   sep = (session_endpoint_cfg_t *) tep;
728   if (!sep->ext_cfg)
729     return SESSION_E_NOEXTCFG;
730
731   app_wrk = app_worker_get (sep->app_wrk_index);
732   app = application_get (app_wrk->app_index);
733
734   clib_memset (args, 0, sizeof (*args));
735   args->app_index = sm->app_index;
736   args->sep_ext = *sep;
737   args->sep_ext.ns_index = app->ns_index;
738   args->sep_ext.transport_proto = TRANSPORT_PROTO_UDP;
739   args->sep_ext.transport_flags = TRANSPORT_CFG_F_CONNECTED;
740   if (vnet_listen (args))
741     return -1;
742
743   lctx_index = srtp_listener_ctx_alloc ();
744   udp_al_handle = args->handle;
745   al = app_listener_get_w_handle (udp_al_handle);
746   srtp_listener = app_listener_get_session (al);
747   srtp_listener->opaque = lctx_index;
748
749   app_listener = listen_session_get (app_listener_index);
750
751   lctx = srtp_listener_ctx_get (lctx_index);
752   lctx->parent_app_wrk_index = sep->app_wrk_index;
753   lctx->srtp_session_handle = udp_al_handle;
754   lctx->app_session_handle = listen_session_get_handle (app_listener);
755   lctx->udp_is_ip4 = sep->is_ip4;
756   lctx->c_s_index = app_listener_index;
757   lctx->c_flags |= TRANSPORT_CONNECTION_F_NO_LOOKUP;
758
759   srtp_init_policy (lctx, (transport_endpt_cfg_srtp_t *) sep->ext_cfg->data);
760
761   SRTP_DBG (1, "Started listening %d", lctx_index);
762   return lctx_index;
763 }
764
765 u32
766 srtp_stop_listen (u32 lctx_index)
767 {
768   session_endpoint_t sep = SESSION_ENDPOINT_NULL;
769   transport_connection_t *lc;
770   srtp_tc_t *lctx;
771   session_t *ls;
772   int rv;
773
774   lctx = srtp_listener_ctx_get (lctx_index);
775
776   /* Cleanup listener from session lookup table */
777   ls = session_get_from_handle (lctx->srtp_session_handle);
778   lc = session_get_transport (ls);
779
780   sep.fib_index = lc->fib_index;
781   sep.port = lc->lcl_port;
782   sep.is_ip4 = lc->is_ip4;
783   sep.transport_proto = TRANSPORT_PROTO_SRTP;
784   clib_memcpy (&sep.ip, &lc->lcl_ip, sizeof (lc->lcl_ip));
785   session_lookup_del_session_endpoint2 (&sep);
786
787   vnet_unlisten_args_t a = {
788     .handle = lctx->srtp_session_handle,
789     .app_index = srtp_main.app_index,
790     .wrk_map_index = 0 /* default wrk */
791   };
792   if ((rv = vnet_unlisten (&a)))
793     SRTP_DBG (0, "unlisten returned %d", rv);
794
795   srtp_listener_ctx_free (lctx);
796   return 0;
797 }
798
799 transport_connection_t *
800 srtp_connection_get (u32 ctx_index, u32 thread_index)
801 {
802   srtp_tc_t *ctx;
803   ctx = srtp_ctx_get_w_thread (ctx_index, thread_index);
804   return &ctx->connection;
805 }
806
807 transport_connection_t *
808 srtp_listener_get (u32 listener_index)
809 {
810   srtp_tc_t *ctx;
811   ctx = srtp_listener_ctx_get (listener_index);
812   return &ctx->connection;
813 }
814
815 int
816 srtp_custom_tx_callback (void *session, transport_send_params_t *sp)
817 {
818   session_t *app_session = (session_t *) session;
819   srtp_tc_t *ctx;
820
821   if (PREDICT_FALSE (app_session->session_state >=
822                      SESSION_STATE_TRANSPORT_CLOSED))
823     return 0;
824
825   ctx = srtp_ctx_get_w_thread (app_session->connection_index,
826                                app_session->thread_index);
827   if (PREDICT_FALSE (ctx->is_migrated))
828     return 0;
829
830   return srtp_ctx_write (ctx, app_session, sp);
831 }
832
833 u8 *
834 format_srtp_ctx (u8 *s, va_list *args)
835 {
836   srtp_tc_t *ctx = va_arg (*args, srtp_tc_t *);
837   u32 udp_si, udp_ti;
838
839   session_parse_handle (ctx->srtp_session_handle, &udp_si, &udp_ti);
840   s = format (s, "[%d:%d][SRTP] app_wrk %u index %u udp %d:%d",
841               ctx->c_thread_index, ctx->c_s_index, ctx->parent_app_wrk_index,
842               ctx->srtp_ctx_handle, udp_ti, udp_si);
843
844   return s;
845 }
846
847 static u8 *
848 format_srtp_listener_ctx (u8 *s, va_list *args)
849 {
850   session_t *udp_listener;
851   app_listener_t *al;
852   srtp_tc_t *ctx;
853
854   ctx = va_arg (*args, srtp_tc_t *);
855
856   al = app_listener_get_w_handle (ctx->srtp_session_handle);
857   udp_listener = app_listener_get_session (al);
858   s = format (s, "[%d:%d][SRTP] app_wrk %u udp %d:%d", ctx->c_thread_index,
859               ctx->c_s_index, ctx->parent_app_wrk_index,
860               udp_listener->thread_index, udp_listener->session_index);
861
862   return s;
863 }
864
865 static u8 *
866 format_srtp_ctx_state (u8 *s, va_list *args)
867 {
868   srtp_tc_t *ctx;
869   session_t *us;
870
871   ctx = va_arg (*args, srtp_tc_t *);
872   us = session_get (ctx->c_s_index, ctx->c_thread_index);
873   if (us->session_state == SESSION_STATE_LISTENING)
874     s = format (s, "%s", "LISTEN");
875   else
876     {
877       if (us->session_state >= SESSION_STATE_TRANSPORT_CLOSED)
878         s = format (s, "%s", "CLOSED");
879       else if (us->session_state == SESSION_STATE_APP_CLOSED)
880         s = format (s, "%s", "APP-CLOSED");
881       else if (us->session_state >= SESSION_STATE_TRANSPORT_CLOSING)
882         s = format (s, "%s", "CLOSING");
883       else
884         s = format (s, "%s", "ESTABLISHED");
885     }
886
887   return s;
888 }
889
890 u8 *
891 format_srtp_connection (u8 *s, va_list *args)
892 {
893   u32 ctx_index = va_arg (*args, u32);
894   u32 thread_index = va_arg (*args, u32);
895   u32 verbose = va_arg (*args, u32);
896   srtp_tc_t *ctx;
897
898   ctx = srtp_ctx_get_w_thread (ctx_index, thread_index);
899   if (!ctx)
900     return s;
901
902   s = format (s, "%-" SESSION_CLI_ID_LEN "U", format_srtp_ctx, ctx);
903   if (verbose)
904     {
905       s =
906         format (s, "%-" SESSION_CLI_STATE_LEN "U", format_srtp_ctx_state, ctx);
907       if (verbose > 1)
908         s = format (s, "\n");
909     }
910   return s;
911 }
912
913 u8 *
914 format_srtp_listener (u8 *s, va_list *args)
915 {
916   u32 tc_index = va_arg (*args, u32);
917   u32 __clib_unused thread_index = va_arg (*args, u32);
918   u32 verbose = va_arg (*args, u32);
919   srtp_tc_t *ctx = srtp_listener_ctx_get (tc_index);
920
921   s = format (s, "%-" SESSION_CLI_ID_LEN "U", format_srtp_listener_ctx, ctx);
922   if (verbose)
923     s = format (s, "%-" SESSION_CLI_STATE_LEN "U", format_srtp_ctx_state, ctx);
924   return s;
925 }
926
927 u8 *
928 format_srtp_half_open (u8 *s, va_list *args)
929 {
930   return 0;
931 }
932
933 static void
934 srtp_transport_endpoint_get (u32 ctx_handle, u32 thread_index,
935                              transport_endpoint_t *tep, u8 is_lcl)
936 {
937   srtp_tc_t *ctx = srtp_ctx_get_w_thread (ctx_handle, thread_index);
938   session_t *udp_session;
939
940   udp_session = session_get_from_handle (ctx->srtp_session_handle);
941   session_get_endpoint (udp_session, tep, is_lcl);
942 }
943
944 static void
945 srtp_transport_listener_endpoint_get (u32 ctx_handle,
946                                       transport_endpoint_t *tep, u8 is_lcl)
947 {
948   session_t *srtp_listener;
949   app_listener_t *al;
950   srtp_tc_t *ctx = srtp_listener_ctx_get (ctx_handle);
951
952   al = app_listener_get_w_handle (ctx->srtp_session_handle);
953   srtp_listener = app_listener_get_session (al);
954   session_get_endpoint (srtp_listener, tep, is_lcl);
955 }
956
957 static const transport_proto_vft_t srtp_proto = {
958   .enable = srtp_enable,
959   .connect = srtp_connect,
960   .close = srtp_disconnect,
961   .start_listen = srtp_start_listen,
962   .stop_listen = srtp_stop_listen,
963   .get_connection = srtp_connection_get,
964   .get_listener = srtp_listener_get,
965   .custom_tx = srtp_custom_tx_callback,
966   .format_connection = format_srtp_connection,
967   .format_half_open = format_srtp_half_open,
968   .format_listener = format_srtp_listener,
969   .get_transport_endpoint = srtp_transport_endpoint_get,
970   .get_transport_listener_endpoint = srtp_transport_listener_endpoint_get,
971   .transport_options = {
972     .name = "srtp",
973     .short_name = "R",
974     .tx_type = TRANSPORT_TX_INTERNAL,
975     .service_type = TRANSPORT_SERVICE_APP,
976   },
977 };
978
979 static clib_error_t *
980 srtp_transport_init (vlib_main_t *vm)
981 {
982   transport_register_protocol (TRANSPORT_PROTO_SRTP, &srtp_proto,
983                                FIB_PROTOCOL_IP4, ~0);
984   transport_register_protocol (TRANSPORT_PROTO_SRTP, &srtp_proto,
985                                FIB_PROTOCOL_IP6, ~0);
986   return 0;
987 }
988
989 VLIB_INIT_FUNCTION (srtp_transport_init);
990
991 VLIB_PLUGIN_REGISTER () = {
992   .version = VPP_BUILD_VER,
993   .description = "Secure Real-time Transport Protocol (SRTP)",
994   .default_disabled = 1,
995 };
996
997 /*
998  * fd.io coding-style-patch-verification: ON
999  *
1000  * Local Variables:
1001  * eval: (c-set-style "gnu")
1002  * End:
1003  */