eeaf2d70088400f66eb693aa298f3ee6d4f88c05
[vpp.git] / src / plugins / hs_apps / echo_server.c
1 /*
2 * Copyright (c) 2017-2019 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 <hs_apps/hs_test.h>
17 #include <vnet/vnet.h>
18 #include <vlibmemory/api.h>
19 #include <vnet/session/application.h>
20 #include <vnet/session/application_interface.h>
21 #include <vnet/session/session.h>
22
23 static void es_set_echo_rx_callbacks (u8 no_echo);
24
25 typedef struct
26 {
27   /*
28    * Server app parameters
29    */
30   svm_msg_q_t **vpp_queue;
31   svm_queue_t *vl_input_queue;  /**< Sever's event queue */
32
33   u32 app_index;                /**< Server app index */
34   u32 my_client_index;          /**< API client handle */
35   u32 node_index;               /**< process node index for event scheduling */
36
37   /*
38    * Config params
39    */
40   hs_test_cfg_t cfg;
41   u32 fifo_size;                /**< Fifo size */
42   u32 rcv_buffer_size;          /**< Rcv buffer size */
43   u32 prealloc_fifos;           /**< Preallocate fifos */
44   u32 private_segment_count;    /**< Number of private segments  */
45   u64 private_segment_size;     /**< Size of private segments  */
46   char *server_uri;             /**< Server URI */
47   u32 tls_engine;               /**< TLS engine: mbedtls/openssl */
48   u32 ckpair_index;             /**< Cert and key for tls/quic */
49   u8 is_dgram;                  /**< set if transport is dgram */
50
51   /*
52    * Test state
53    */
54   int (*rx_callback) (session_t *session);
55   u64 **session_handles;
56   u8 **rx_buf;                  /**< Per-thread RX buffer */
57   u32 **rx_retries;
58   u8 byte_index;
59   u8 transport_proto;
60   u64 listener_handle;          /**< Session handle of the root listener */
61   u64 ctrl_listener_handle;
62
63   vlib_main_t *vlib_main;
64 } echo_server_main_t;
65
66 echo_server_main_t echo_server_main;
67
68 #define es_err(_fmt, _args...) clib_warning (_fmt, ##_args);
69
70 #define es_dbg(_fmt, _args...)                                                \
71   do                                                                          \
72     {                                                                         \
73       if (PREDICT_FALSE (echo_server_main.cfg.verbose))                       \
74         es_err (_fmt, ##_args);                                               \
75     }                                                                         \
76   while (0)
77
78 #define es_cli(_fmt, _args...) vlib_cli_output (vm, _fmt, ##_args)
79
80 int
81 quic_echo_server_qsession_accept_callback (session_t * s)
82 {
83   es_dbg ("QSession %u accept w/opaque %d", s->session_index, s->opaque);
84   return 0;
85 }
86
87 static int
88 echo_server_ctrl_session_accept_callback (session_t *s)
89 {
90   s->session_state = SESSION_STATE_READY;
91   return 0;
92 }
93
94 int
95 quic_echo_server_session_accept_callback (session_t * s)
96 {
97   echo_server_main_t *esm = &echo_server_main;
98
99   if (PREDICT_FALSE (esm->ctrl_listener_handle == s->listener_handle))
100     return echo_server_ctrl_session_accept_callback (s);
101
102   if (s->listener_handle == esm->listener_handle)
103     return quic_echo_server_qsession_accept_callback (s);
104
105   es_dbg ("SSESSION %u accept w/opaque %d", s->session_index, s->opaque);
106
107   esm->vpp_queue[s->thread_index] =
108     session_main_get_vpp_event_queue (s->thread_index);
109   s->session_state = SESSION_STATE_READY;
110   ASSERT (vec_len (esm->rx_retries) > s->thread_index);
111   vec_validate (esm->rx_retries[s->thread_index], s->session_index);
112   esm->rx_retries[s->thread_index][s->session_index] = 0;
113   return 0;
114 }
115
116 int
117 echo_server_session_accept_callback (session_t * s)
118 {
119   echo_server_main_t *esm = &echo_server_main;
120
121   if (PREDICT_FALSE (esm->ctrl_listener_handle == s->listener_handle))
122     return echo_server_ctrl_session_accept_callback (s);
123
124   esm->vpp_queue[s->thread_index] =
125     session_main_get_vpp_event_queue (s->thread_index);
126   s->session_state = SESSION_STATE_READY;
127   ASSERT (vec_len (esm->rx_retries) > s->thread_index);
128   vec_validate (esm->rx_retries[s->thread_index], s->session_index);
129   esm->rx_retries[s->thread_index][s->session_index] = 0;
130   if (session_get_transport_proto (s) == TRANSPORT_PROTO_UDP)
131     {
132       vec_add1 (esm->session_handles[s->thread_index], session_handle (s));
133     }
134   return 0;
135 }
136
137 void
138 echo_server_session_disconnect_callback (session_t * s)
139 {
140   echo_server_main_t *esm = &echo_server_main;
141   vnet_disconnect_args_t _a = { 0 }, *a = &_a;
142
143   a->handle = session_handle (s);
144   a->app_index = esm->app_index;
145   vnet_disconnect_session (a);
146 }
147
148 void
149 echo_server_session_reset_callback (session_t * s)
150 {
151   echo_server_main_t *esm = &echo_server_main;
152   vnet_disconnect_args_t _a = { 0 }, *a = &_a;
153   es_dbg ("Reset session %U", format_session, s, 2);
154   a->handle = session_handle (s);
155   a->app_index = esm->app_index;
156   vnet_disconnect_session (a);
157 }
158
159 int
160 echo_server_session_connected_callback (u32 app_index, u32 api_context,
161                                         session_t * s, session_error_t err)
162 {
163   es_err ("called...");
164   return -1;
165 }
166
167 int
168 echo_server_add_segment_callback (u32 client_index, u64 segment_handle)
169 {
170   /* New heaps may be added */
171   return 0;
172 }
173
174 int
175 echo_server_redirect_connect_callback (u32 client_index, void *mp)
176 {
177   es_err ("called...");
178   return -1;
179 }
180
181 static void
182 es_foreach_thread (void *fp)
183 {
184   echo_server_main_t *esm = &echo_server_main;
185   uword thread_index;
186   for (thread_index = 0; thread_index < vec_len (esm->session_handles);
187        thread_index++)
188     {
189       session_send_rpc_evt_to_thread (thread_index, fp,
190                                       uword_to_pointer (thread_index, void *));
191     }
192 }
193
194 static int
195 es_wrk_prealloc_sessions (void *args)
196 {
197   echo_server_main_t *esm = &echo_server_main;
198   u32 thread_index = pointer_to_uword (args);
199
200   vec_validate (esm->rx_retries[thread_index], esm->cfg.num_test_sessions);
201
202   return 0;
203 }
204
205 static int
206 echo_server_setup_test (hs_test_cfg_t *c)
207 {
208   echo_server_main_t *esm = &echo_server_main;
209
210   if (c->test == HS_TEST_TYPE_UNI)
211     es_set_echo_rx_callbacks (1 /* no echo */);
212   else
213     es_set_echo_rx_callbacks (0 /* no echo */);
214
215   es_foreach_thread (es_wrk_prealloc_sessions);
216
217   if (c->test_bytes && c->num_test_sessions > 1)
218     {
219       es_err ("test bytes not supported for more sessions; turning it off");
220       c->test_bytes = 0;
221     }
222   esm->byte_index = 0;
223
224   return 0;
225 }
226
227 static void
228 echo_server_ctrl_reply (session_t *s)
229 {
230   echo_server_main_t *esm = &echo_server_main;
231   int rv;
232
233   rv = svm_fifo_enqueue (s->tx_fifo, sizeof (esm->cfg), (u8 *) &esm->cfg);
234   ASSERT (rv == sizeof (esm->cfg));
235   session_send_io_evt_to_thread_custom (&s->session_index, s->thread_index,
236                                         SESSION_IO_EVT_TX);
237 }
238
239 static int
240 es_test_cmd_sync (echo_server_main_t *esm, session_t *s)
241 {
242   int rv;
243
244   rv = echo_server_setup_test (&esm->cfg);
245   if (rv)
246     es_err ("setup test error!");
247
248   echo_server_ctrl_reply (s);
249   return 0;
250 }
251
252 static int
253 es_wrk_cleanup_session (void *args)
254 {
255   echo_server_main_t *esm = &echo_server_main;
256   u32 thread_index = pointer_to_uword (args);
257   session_handle_t *session_handles, *sh;
258   vnet_disconnect_args_t _a = {}, *a = &_a;
259
260   a->app_index = esm->app_index;
261
262   session_handles = esm->session_handles[thread_index];
263
264   vec_foreach (sh, session_handles)
265     {
266       a->handle = sh[0];
267       vnet_disconnect_session (a);
268     }
269   vec_reset_length (session_handles);
270   return 0;
271 }
272
273 static int
274 echo_server_rx_ctrl_callback (session_t *s)
275 {
276   echo_server_main_t *esm = &echo_server_main;
277   int rv;
278
279   rv = svm_fifo_dequeue (s->rx_fifo, sizeof (esm->cfg), (u8 *) &esm->cfg);
280   ASSERT (rv == sizeof (esm->cfg));
281
282   es_dbg ("control message received:");
283   if (esm->cfg.verbose)
284     hs_test_cfg_dump (&esm->cfg, 0);
285
286   switch (esm->cfg.cmd)
287     {
288     case HS_TEST_CMD_SYNC:
289       switch (esm->cfg.test)
290         {
291         case HS_TEST_TYPE_ECHO:
292         case HS_TEST_TYPE_NONE:
293           es_foreach_thread (es_wrk_cleanup_session);
294           echo_server_ctrl_reply (s);
295           break;
296         case HS_TEST_TYPE_UNI:
297         case HS_TEST_TYPE_BI:
298           return es_test_cmd_sync (esm, s);
299           break;
300         default:
301           es_err ("unknown command type! %d", esm->cfg.cmd);
302         }
303       break;
304     case HS_TEST_CMD_START:
305     case HS_TEST_CMD_STOP:
306       echo_server_ctrl_reply (s);
307       break;
308     default:
309       es_err ("unknown command! %d", esm->cfg.cmd);
310       break;
311     }
312   return 0;
313 }
314
315 /*
316  * If no-echo, just drop the data and be done with it.
317  */
318 int
319 echo_server_builtin_server_rx_callback_no_echo (session_t * s)
320 {
321   echo_server_main_t *esm = &echo_server_main;
322   if (PREDICT_FALSE (esm->ctrl_listener_handle == s->listener_handle))
323     return echo_server_rx_ctrl_callback (s);
324
325   svm_fifo_t *rx_fifo = s->rx_fifo;
326   svm_fifo_dequeue_drop (rx_fifo, svm_fifo_max_dequeue_cons (rx_fifo));
327   return 0;
328 }
329
330 static void
331 es_test_bytes (echo_server_main_t *esm, int actual_transfer)
332 {
333   int i;
334   u32 my_thread_id = vlib_get_thread_index ();
335   for (i = 0; i < actual_transfer; i++)
336     {
337       if (esm->rx_buf[my_thread_id][i] != ((esm->byte_index + i) & 0xff))
338         {
339           es_err ("at %lld expected %d got %d", esm->byte_index + i,
340                   (esm->byte_index + i) & 0xff, esm->rx_buf[my_thread_id][i]);
341         }
342     }
343   esm->byte_index += actual_transfer;
344 }
345
346 int
347 echo_server_rx_callback (session_t * s)
348 {
349   u32 n_written, max_dequeue, max_enqueue, max_transfer;
350   int actual_transfer;
351   svm_fifo_t *tx_fifo, *rx_fifo;
352   echo_server_main_t *esm = &echo_server_main;
353   u32 thread_index = vlib_get_thread_index ();
354   app_session_transport_t at;
355
356   ASSERT (s->thread_index == thread_index);
357
358   rx_fifo = s->rx_fifo;
359   tx_fifo = s->tx_fifo;
360
361   ASSERT (rx_fifo->master_thread_index == thread_index);
362   ASSERT (tx_fifo->master_thread_index == thread_index);
363
364   if (PREDICT_FALSE (esm->ctrl_listener_handle == s->listener_handle))
365     return echo_server_rx_ctrl_callback (s);
366
367   max_enqueue = svm_fifo_max_enqueue_prod (tx_fifo);
368   if (!esm->is_dgram)
369     {
370       max_dequeue = svm_fifo_max_dequeue_cons (rx_fifo);
371     }
372   else
373     {
374       session_dgram_pre_hdr_t ph;
375       svm_fifo_peek (rx_fifo, 0, sizeof (ph), (u8 *) & ph);
376       max_dequeue = ph.data_length - ph.data_offset;
377       if (!esm->vpp_queue[s->thread_index])
378         {
379           svm_msg_q_t *mq;
380           mq = session_main_get_vpp_event_queue (s->thread_index);
381           esm->vpp_queue[s->thread_index] = mq;
382         }
383       max_enqueue -= sizeof (session_dgram_hdr_t);
384     }
385
386   if (PREDICT_FALSE (max_dequeue == 0))
387     return 0;
388
389   /* Number of bytes we're going to copy */
390   max_transfer = clib_min (max_dequeue, max_enqueue);
391
392   /* No space in tx fifo */
393   if (PREDICT_FALSE (max_transfer == 0))
394     {
395       /* XXX timeout for session that are stuck */
396
397     rx_event:
398       /* Program self-tap to retry */
399       if (svm_fifo_set_event (rx_fifo))
400         {
401           /* TODO should be session_enqueue_notify(s) but quic tests seem
402            * to fail if that's the case */
403           if (session_send_io_evt_to_thread (rx_fifo,
404                                              SESSION_IO_EVT_BUILTIN_RX))
405             es_err ("failed to enqueue self-tap");
406
407           vec_validate (esm->rx_retries[s->thread_index], s->session_index);
408           if (esm->rx_retries[thread_index][s->session_index] == 500000)
409             {
410               es_err ("session stuck: %U", format_session, s, 2);
411             }
412           if (esm->rx_retries[thread_index][s->session_index] < 500001)
413             esm->rx_retries[thread_index][s->session_index]++;
414         }
415
416       return 0;
417     }
418
419   vec_validate (esm->rx_buf[thread_index], max_transfer);
420   if (!esm->is_dgram)
421     {
422       actual_transfer = app_recv_stream_raw (rx_fifo,
423                                              esm->rx_buf[thread_index],
424                                              max_transfer,
425                                              0 /* don't clear event */ ,
426                                              0 /* peek */ );
427     }
428   else
429     {
430       actual_transfer = app_recv_dgram_raw (rx_fifo,
431                                             esm->rx_buf[thread_index],
432                                             max_transfer, &at,
433                                             0 /* don't clear event */ ,
434                                             0 /* peek */ );
435     }
436   ASSERT (actual_transfer == max_transfer);
437
438   if (esm->cfg.test_bytes)
439     {
440       es_test_bytes (esm, actual_transfer);
441     }
442
443   /*
444    * Echo back
445    */
446
447   if (!esm->is_dgram)
448     {
449       n_written = app_send_stream_raw (tx_fifo,
450                                        esm->vpp_queue[thread_index],
451                                        esm->rx_buf[thread_index],
452                                        actual_transfer, SESSION_IO_EVT_TX,
453                                        1 /* do_evt */ , 0);
454     }
455   else
456     {
457       n_written = app_send_dgram_raw (tx_fifo, &at,
458                                       esm->vpp_queue[s->thread_index],
459                                       esm->rx_buf[thread_index],
460                                       actual_transfer, SESSION_IO_EVT_TX,
461                                       1 /* do_evt */ , 0);
462     }
463
464   if (n_written != max_transfer)
465     es_err ("short trout! written %u read %u", n_written, max_transfer);
466
467   if (PREDICT_FALSE (svm_fifo_max_dequeue_cons (rx_fifo)))
468     goto rx_event;
469
470   return 0;
471 }
472
473 int
474 echo_server_rx_callback_common (session_t *s)
475 {
476   echo_server_main_t *esm = &echo_server_main;
477   return esm->rx_callback (s);
478 }
479
480 static session_cb_vft_t echo_server_session_cb_vft = {
481   .session_accept_callback = echo_server_session_accept_callback,
482   .session_disconnect_callback = echo_server_session_disconnect_callback,
483   .session_connected_callback = echo_server_session_connected_callback,
484   .add_segment_callback = echo_server_add_segment_callback,
485   .builtin_app_rx_callback = echo_server_rx_callback_common,
486   .session_reset_callback = echo_server_session_reset_callback
487 };
488
489 static void
490 es_set_echo_rx_callbacks (u8 no_echo)
491 {
492   echo_server_main_t *esm = &echo_server_main;
493   if (no_echo)
494     esm->rx_callback = echo_server_builtin_server_rx_callback_no_echo;
495   else
496     esm->rx_callback = echo_server_rx_callback;
497 }
498
499 static int
500 echo_server_attach (u8 * appns_id, u64 appns_flags, u64 appns_secret)
501 {
502   vnet_app_add_cert_key_pair_args_t _ck_pair, *ck_pair = &_ck_pair;
503   echo_server_main_t *esm = &echo_server_main;
504   vnet_app_attach_args_t _a, *a = &_a;
505   u64 options[APP_OPTIONS_N_OPTIONS];
506
507   clib_memset (a, 0, sizeof (*a));
508   clib_memset (options, 0, sizeof (options));
509
510   esm->rx_callback = echo_server_rx_callback;
511
512   if (esm->transport_proto == TRANSPORT_PROTO_QUIC)
513     echo_server_session_cb_vft.session_accept_callback =
514       quic_echo_server_session_accept_callback;
515
516   a->api_client_index = ~0;
517   a->name = format (0, "echo_server");
518   a->session_cb_vft = &echo_server_session_cb_vft;
519   a->options = options;
520   a->options[APP_OPTIONS_SEGMENT_SIZE] = esm->private_segment_size;
521   a->options[APP_OPTIONS_ADD_SEGMENT_SIZE] = esm->private_segment_size;
522   a->options[APP_OPTIONS_RX_FIFO_SIZE] = esm->fifo_size;
523   a->options[APP_OPTIONS_TX_FIFO_SIZE] = esm->fifo_size;
524   a->options[APP_OPTIONS_PRIVATE_SEGMENT_COUNT] = esm->private_segment_count;
525   a->options[APP_OPTIONS_TLS_ENGINE] = esm->tls_engine;
526   a->options[APP_OPTIONS_PCT_FIRST_ALLOC] = 100;
527   a->options[APP_OPTIONS_PREALLOC_FIFO_PAIRS] =
528     esm->prealloc_fifos ? esm->prealloc_fifos : 1;
529
530   a->options[APP_OPTIONS_FLAGS] = APP_OPTIONS_FLAGS_IS_BUILTIN;
531   if (appns_id)
532     {
533       a->namespace_id = appns_id;
534       a->options[APP_OPTIONS_FLAGS] |= appns_flags;
535       a->options[APP_OPTIONS_NAMESPACE_SECRET] = appns_secret;
536     }
537
538   if (vnet_application_attach (a))
539     {
540       es_err ("failed to attach server");
541       return -1;
542     }
543   esm->app_index = a->app_index;
544   vec_free (a->name);
545
546   clib_memset (ck_pair, 0, sizeof (*ck_pair));
547   ck_pair->cert = (u8 *) test_srv_crt_rsa;
548   ck_pair->key = (u8 *) test_srv_key_rsa;
549   ck_pair->cert_len = test_srv_crt_rsa_len;
550   ck_pair->key_len = test_srv_key_rsa_len;
551   vnet_app_add_cert_key_pair (ck_pair);
552   esm->ckpair_index = ck_pair->index;
553
554   return 0;
555 }
556
557 static int
558 echo_server_detach (void)
559 {
560   echo_server_main_t *esm = &echo_server_main;
561   vnet_app_detach_args_t _da, *da = &_da;
562   int rv;
563
564   da->app_index = esm->app_index;
565   da->api_client_index = ~0;
566   rv = vnet_application_detach (da);
567   esm->app_index = ~0;
568   vnet_app_del_cert_key_pair (esm->ckpair_index);
569   return rv;
570 }
571
572 static int
573 echo_client_transport_needs_crypto (transport_proto_t proto)
574 {
575   return proto == TRANSPORT_PROTO_TLS || proto == TRANSPORT_PROTO_DTLS ||
576          proto == TRANSPORT_PROTO_QUIC;
577 }
578
579 static int
580 echo_server_listen_ctrl ()
581 {
582   echo_server_main_t *esm = &echo_server_main;
583   vnet_listen_args_t _args = {}, *args = &_args;
584   session_error_t rv;
585
586   if ((rv = parse_uri (esm->server_uri, &args->sep_ext)))
587     return -1;
588   args->sep_ext.transport_proto = TRANSPORT_PROTO_TCP;
589   args->app_index = esm->app_index;
590
591   rv = vnet_listen (args);
592   esm->ctrl_listener_handle = args->handle;
593   return rv;
594 }
595
596 static int
597 echo_server_listen ()
598 {
599   i32 rv;
600   echo_server_main_t *esm = &echo_server_main;
601   vnet_listen_args_t _args = {}, *args = &_args;
602
603   if ((rv = parse_uri (esm->server_uri, &args->sep_ext)))
604     {
605       return -1;
606     }
607   args->app_index = esm->app_index;
608   args->sep_ext.port = hs_make_data_port (args->sep_ext.port);
609   if (echo_client_transport_needs_crypto (args->sep_ext.transport_proto))
610     {
611       session_endpoint_alloc_ext_cfg (&args->sep_ext,
612                                       TRANSPORT_ENDPT_EXT_CFG_CRYPTO);
613       args->sep_ext.ext_cfg->crypto.ckpair_index = esm->ckpair_index;
614     }
615
616   if (args->sep_ext.transport_proto == TRANSPORT_PROTO_UDP)
617     {
618       args->sep_ext.transport_flags = TRANSPORT_CFG_F_CONNECTED;
619     }
620
621   rv = vnet_listen (args);
622   esm->listener_handle = args->handle;
623   if (args->sep_ext.ext_cfg)
624     clib_mem_free (args->sep_ext.ext_cfg);
625   return rv;
626 }
627
628 static int
629 echo_server_create (vlib_main_t * vm, u8 * appns_id, u64 appns_flags,
630                     u64 appns_secret)
631 {
632   echo_server_main_t *esm = &echo_server_main;
633   vlib_thread_main_t *vtm = vlib_get_thread_main ();
634   u32 num_threads;
635   int i;
636
637   num_threads = 1 /* main thread */  + vtm->n_threads;
638   vec_validate (echo_server_main.vpp_queue, num_threads - 1);
639   vec_validate (esm->rx_buf, num_threads - 1);
640   vec_validate (esm->rx_retries, num_threads - 1);
641   vec_validate (esm->session_handles, num_threads - 1);
642   for (i = 0; i < vec_len (esm->rx_retries); i++)
643     {
644       vec_validate (esm->rx_retries[i],
645                     pool_elts (session_main.wrk[i].sessions));
646       vec_validate (esm->session_handles[i],
647                     pool_elts (session_main.wrk[i].sessions));
648       clib_memset (esm->session_handles[i], ~0,
649                    sizeof (u64) * vec_len (esm->session_handles[i]));
650       vec_reset_length (esm->session_handles[i]);
651     }
652   esm->rcv_buffer_size = clib_max (esm->rcv_buffer_size, esm->fifo_size);
653   for (i = 0; i < num_threads; i++)
654     vec_validate (esm->rx_buf[i], esm->rcv_buffer_size);
655
656   if (echo_server_attach (appns_id, appns_flags, appns_secret))
657     {
658       es_err ("failed to attach server");
659       return -1;
660     }
661   if (echo_server_listen_ctrl ())
662     {
663       es_err ("failed to start listening on ctrl session");
664       if (echo_server_detach ())
665         es_err ("failed to detach");
666       return -1;
667     }
668   if (echo_server_listen ())
669     {
670       es_err ("failed to start listening");
671       if (echo_server_detach ())
672         es_err ("failed to detach");
673       return -1;
674     }
675   return 0;
676 }
677
678 static clib_error_t *
679 echo_server_create_command_fn (vlib_main_t * vm, unformat_input_t * input,
680                                vlib_cli_command_t * cmd)
681 {
682   session_endpoint_cfg_t sep = SESSION_ENDPOINT_CFG_NULL;
683   echo_server_main_t *esm = &echo_server_main;
684   u8 server_uri_set = 0, *appns_id = 0;
685   u64 appns_flags = 0, appns_secret = 0;
686   char *default_uri = "tcp://0.0.0.0/1234";
687   int rv, is_stop = 0;
688   clib_error_t *error = 0;
689
690   esm->fifo_size = 64 << 10;
691   esm->rcv_buffer_size = 128 << 10;
692   esm->prealloc_fifos = 0;
693   esm->private_segment_count = 0;
694   esm->private_segment_size = 512 << 20;
695   esm->tls_engine = CRYPTO_ENGINE_OPENSSL;
696   vec_free (esm->server_uri);
697
698   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
699     {
700       if (unformat (input, "uri %s", &esm->server_uri))
701         server_uri_set = 1;
702       else if (unformat (input, "fifo-size %U", unformat_memory_size,
703                          &esm->fifo_size))
704         ;
705       else if (unformat (input, "rcv-buf-size %d", &esm->rcv_buffer_size))
706         ;
707       else if (unformat (input, "prealloc-fifos %d", &esm->prealloc_fifos))
708         ;
709       else if (unformat (input, "private-segment-count %d",
710                          &esm->private_segment_count))
711         ;
712       else if (unformat (input, "private-segment-size %U",
713                          unformat_memory_size, &esm->private_segment_size))
714         ;
715       else if (unformat (input, "appns %_%v%_", &appns_id))
716         ;
717       else if (unformat (input, "all-scope"))
718         appns_flags |= (APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE
719                         | APP_OPTIONS_FLAGS_USE_LOCAL_SCOPE);
720       else if (unformat (input, "local-scope"))
721         appns_flags |= APP_OPTIONS_FLAGS_USE_LOCAL_SCOPE;
722       else if (unformat (input, "global-scope"))
723         appns_flags |= APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE;
724       else if (unformat (input, "secret %lu", &appns_secret))
725         ;
726       else if (unformat (input, "stop"))
727         is_stop = 1;
728       else if (unformat (input, "tls-engine %d", &esm->tls_engine))
729         ;
730       else
731         {
732           error = clib_error_return (0, "failed: unknown input `%U'",
733                                      format_unformat_error, input);
734           goto cleanup;
735         }
736     }
737
738   if (is_stop)
739     {
740       if (esm->app_index == (u32) ~ 0)
741         {
742           es_cli ("server not running");
743           error = clib_error_return (0, "failed: server not running");
744           goto cleanup;
745         }
746       rv = echo_server_detach ();
747       if (rv)
748         {
749           es_cli ("failed: detach");
750           error = clib_error_return (0, "failed: server detach %d", rv);
751           goto cleanup;
752         }
753       goto cleanup;
754     }
755
756   vnet_session_enable_disable (vm, 1 /* turn on TCP, etc. */ );
757
758   if (!server_uri_set)
759     {
760       es_cli ("No uri provided! Using default: %s", default_uri);
761       esm->server_uri = (char *) format (0, "%s%c", default_uri, 0);
762     }
763
764   if ((rv = parse_uri ((char *) esm->server_uri, &sep)))
765     {
766       error = clib_error_return (0, "Uri parse error: %d", rv);
767       goto cleanup;
768     }
769   esm->transport_proto = sep.transport_proto;
770   esm->is_dgram = (sep.transport_proto == TRANSPORT_PROTO_UDP);
771
772   rv = echo_server_create (vm, appns_id, appns_flags, appns_secret);
773   if (rv)
774     {
775       vec_free (esm->server_uri);
776       error = clib_error_return (0, "failed: server_create returned %d", rv);
777       goto cleanup;
778     }
779
780 cleanup:
781   vec_free (appns_id);
782
783   return error;
784 }
785
786 /* *INDENT-OFF* */
787 VLIB_CLI_COMMAND (echo_server_create_command, static) = {
788   .path = "test echo server",
789   .short_help =
790     "test echo server proto <proto> [fifo-size <mbytes>]"
791     "[rcv-buf-size <bytes>][prealloc-fifos <count>]"
792     "[private-segment-count <count>][private-segment-size <bytes[m|g]>]"
793     "[uri <tcp://ip/port>]",
794   .function = echo_server_create_command_fn,
795 };
796 /* *INDENT-ON* */
797
798 clib_error_t *
799 echo_server_main_init (vlib_main_t * vm)
800 {
801   echo_server_main_t *esm = &echo_server_main;
802   esm->my_client_index = ~0;
803   return 0;
804 }
805
806 VLIB_INIT_FUNCTION (echo_server_main_init);
807
808 /*
809 * fd.io coding-style-patch-verification: ON
810 *
811 * Local Variables:
812 * eval: (c-set-style "gnu")
813 * End:
814 */