session_cli.c revision 91f90d08
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#include <vnet/session/application.h>
16#include <vnet/session/session.h>
17
18u8 *
19format_session_fifos (u8 * s, va_list * args)
20{
21  session_t *ss = va_arg (*args, session_t *);
22  int verbose = va_arg (*args, int);
23  session_event_t _e, *e = &_e;
24  u8 found;
25
26  if (!ss->rx_fifo || !ss->tx_fifo)
27    return s;
28
29  s = format (s, " Rx fifo: %U", format_svm_fifo, ss->rx_fifo, verbose);
30  if (verbose > 2 && ss->rx_fifo->has_event)
31    {
32      found = session_node_lookup_fifo_event (ss->rx_fifo, e);
33      s = format (s, " session node event: %s\n",
34		  found ? "found" : "not found");
35    }
36  s = format (s, " Tx fifo: %U", format_svm_fifo, ss->tx_fifo, verbose);
37  if (verbose > 2 && ss->tx_fifo->has_event)
38    {
39      found = session_node_lookup_fifo_event (ss->tx_fifo, e);
40      s = format (s, " session node event: %s\n",
41		  found ? "found" : "not found");
42    }
43  return s;
44}
45
46const char *session_state_str[] = {
47#define _(sym, str) str,
48  foreach_session_state
49#undef _
50};
51
52u8 *
53format_session_state (u8 * s, va_list * args)
54{
55  session_t *ss = va_arg (*args, session_t *);
56
57  if (ss->session_state < SESSION_N_STATES)
58    s = format (s, "%s", session_state_str[ss->session_state]);
59  else
60    s = format (s, "UNKNOWN STATE (%d)", ss->session_state);
61
62  return s;
63}
64
65const char *session_flags_str[] = {
66#define _(sym, str) str,
67  foreach_session_flag
68#undef _
69};
70
71u8 *
72format_session_flags (u8 * s, va_list * args)
73{
74  session_t *ss = va_arg (*args, session_t *);
75  int i, last = -1;
76
77  for (i = 0; i < SESSION_N_FLAGS; i++)
78    if (ss->flags & (1 << i))
79      last = i;
80
81  for (i = 0; i < last; i++)
82    {
83      if (ss->flags & (1 << i))
84	s = format (s, "%s, ", session_flags_str[i]);
85    }
86  if (last >= 0)
87    s = format (s, "%s", session_flags_str[last]);
88
89  return s;
90}
91
92/**
93 * Format stream session as per the following format
94 *
95 * verbose:
96 *   "Connection", "Rx fifo", "Tx fifo", "Session Index"
97 * non-verbose:
98 *   "Connection"
99 */
100u8 *
101format_session (u8 * s, va_list * args)
102{
103  session_t *ss = va_arg (*args, session_t *);
104  int verbose = va_arg (*args, int);
105  u32 tp = session_get_transport_proto (ss);
106  u8 *str = 0;
107
108  if (ss->session_state >= SESSION_STATE_TRANSPORT_DELETED)
109    {
110      s = format (s, "[%u:%u] CLOSED", ss->thread_index, ss->session_index);
111      return s;
112    }
113
114  if (verbose == 1)
115    {
116      u8 post_accept = ss->session_state >= SESSION_STATE_ACCEPTING;
117      u8 hasf = post_accept
118	|| session_transport_service_type (ss) == TRANSPORT_SERVICE_CL;
119      u32 rxf, txf;
120
121      rxf = hasf ? svm_fifo_max_dequeue (ss->rx_fifo) : 0;
122      txf = hasf ? svm_fifo_max_dequeue (ss->tx_fifo) : 0;
123      str = format (0, "%-10u%-10u", rxf, txf);
124    }
125
126  if (ss->session_state >= SESSION_STATE_ACCEPTING
127      || ss->session_state == SESSION_STATE_CREATED)
128    {
129      s = format (s, "%U", format_transport_connection, tp,
130		  ss->connection_index, ss->thread_index, verbose);
131      if (verbose == 1)
132	s = format (s, "%v", str);
133      if (verbose > 1)
134	{
135	  s = format (s, "%U", format_session_fifos, ss, verbose);
136	  s = format (s, " session: state: %U flags: %U\n",
137		      format_session_state, ss, format_session_flags, ss);
138	}
139    }
140  else if (ss->session_state == SESSION_STATE_LISTENING)
141    {
142      s = format (s, "%U%v", format_transport_listen_connection,
143		  tp, ss->connection_index, ss->thread_index, verbose, str);
144      if (verbose > 1)
145	s = format (s, "\n%U", format_session_fifos, ss, verbose);
146    }
147  else if (ss->session_state == SESSION_STATE_CONNECTING)
148    {
149      s = format (s, "%-40U%v", format_transport_half_open_connection,
150		  tp, ss->connection_index, ss->thread_index, str);
151    }
152  else
153    {
154      clib_warning ("Session in state: %d!", ss->session_state);
155    }
156  vec_free (str);
157
158  return s;
159}
160
161uword
162unformat_stream_session_id (unformat_input_t * input, va_list * args)
163{
164  u8 *proto = va_arg (*args, u8 *);
165  u32 *fib_index = va_arg (*args, u32 *);
166  ip46_address_t *lcl = va_arg (*args, ip46_address_t *);
167  ip46_address_t *rmt = va_arg (*args, ip46_address_t *);
168  u16 *lcl_port = va_arg (*args, u16 *);
169  u16 *rmt_port = va_arg (*args, u16 *);
170  u8 *is_ip4 = va_arg (*args, u8 *);
171  u8 tuple_is_set = 0;
172  u32 vrf = ~0;
173
174  clib_memset (lcl, 0, sizeof (*lcl));
175  clib_memset (rmt, 0, sizeof (*rmt));
176
177  if (unformat (input, "tcp"))
178    {
179      *proto = TRANSPORT_PROTO_TCP;
180    }
181  else if (unformat (input, "udp"))
182    {
183      *proto = TRANSPORT_PROTO_UDP;
184    }
185  else
186    return 0;
187
188  if (unformat (input, "vrf %u", &vrf))
189    ;
190
191  if (unformat (input, "%U:%d->%U:%d", unformat_ip4_address, &lcl->ip4,
192		lcl_port, unformat_ip4_address, &rmt->ip4, rmt_port))
193    {
194      *is_ip4 = 1;
195      tuple_is_set = 1;
196    }
197  else if (unformat (input, "%U:%d->%U:%d", unformat_ip6_address, &lcl->ip6,
198		     lcl_port, unformat_ip6_address, &rmt->ip6, rmt_port))
199    {
200      *is_ip4 = 0;
201      tuple_is_set = 1;
202    }
203
204  if (vrf != ~0)
205    {
206      fib_protocol_t fib_proto;
207      fib_proto = *is_ip4 ? FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6;
208      *fib_index = fib_table_find (fib_proto, vrf);
209    }
210
211  return tuple_is_set;
212}
213
214uword
215unformat_session_state (unformat_input_t * input, va_list * args)
216{
217  session_state_t *state = va_arg (*args, session_state_t *);
218  u8 *state_vec = 0;
219  int rv = 0;
220
221#define _(sym, str)					\
222  if (unformat (input, str))				\
223    {							\
224      *state = SESSION_STATE_ ## sym;			\
225      rv = 1;						\
226      goto done;					\
227    }
228  foreach_session_state
229#undef _
230done:
231  vec_free (state_vec);
232  return rv;
233}
234
235uword
236unformat_session (unformat_input_t * input, va_list * args)
237{
238  session_t **result = va_arg (*args, session_t **);
239  u32 lcl_port = 0, rmt_port = 0, fib_index = 0;
240  ip46_address_t lcl, rmt;
241  session_t *s;
242  u8 proto = ~0;
243  u8 is_ip4 = 0;
244
245  if (!unformat (input, "%U", unformat_stream_session_id, &proto, &fib_index,
246		 &lcl, &rmt, &lcl_port, &rmt_port, &is_ip4))
247    return 0;
248
249  if (is_ip4)
250    s = session_lookup_safe4 (fib_index, &lcl.ip4, &rmt.ip4,
251			      clib_host_to_net_u16 (lcl_port),
252			      clib_host_to_net_u16 (rmt_port), proto);
253  else
254    s = session_lookup_safe6 (fib_index, &lcl.ip6, &rmt.ip6,
255			      clib_host_to_net_u16 (lcl_port),
256			      clib_host_to_net_u16 (rmt_port), proto);
257  if (s)
258    {
259      *result = s;
260      session_pool_remove_peeker (s->thread_index);
261      return 1;
262    }
263  return 0;
264}
265
266uword
267unformat_transport_connection (unformat_input_t * input, va_list * args)
268{
269  transport_connection_t **result = va_arg (*args, transport_connection_t **);
270  u32 suggested_proto = va_arg (*args, u32);
271  transport_connection_t *tc;
272  u8 proto = ~0;
273  ip46_address_t lcl, rmt;
274  u32 lcl_port = 0, rmt_port = 0, fib_index = 0;
275  u8 is_ip4 = 0;
276
277  if (!unformat (input, "%U", unformat_stream_session_id, &fib_index, &proto,
278		 &lcl, &rmt, &lcl_port, &rmt_port, &is_ip4))
279    return 0;
280
281  proto = (proto == (u8) ~ 0) ? suggested_proto : proto;
282  if (proto == (u8) ~ 0)
283    return 0;
284  if (is_ip4)
285    tc = session_lookup_connection4 (fib_index, &lcl.ip4, &rmt.ip4,
286				     clib_host_to_net_u16 (lcl_port),
287				     clib_host_to_net_u16 (rmt_port), proto);
288  else
289    tc = session_lookup_connection6 (fib_index, &lcl.ip6, &rmt.ip6,
290				     clib_host_to_net_u16 (lcl_port),
291				     clib_host_to_net_u16 (rmt_port), proto);
292
293  if (tc)
294    {
295      *result = tc;
296      return 1;
297    }
298  return 0;
299}
300
301static void
302session_cli_show_all_sessions (vlib_main_t * vm, int verbose)
303{
304  session_main_t *smm = &session_main;
305  u32 n_closed, thread_index;
306  session_t *pool, *s;
307
308  for (thread_index = 0; thread_index < vec_len (smm->wrk); thread_index++)
309    {
310      pool = smm->wrk[thread_index].sessions;
311
312      if (!pool_elts (pool))
313	{
314	  vlib_cli_output (vm, "Thread %d: no sessions", thread_index);
315	  continue;
316	}
317
318      if (!verbose)
319	{
320	  vlib_cli_output (vm, "Thread %d: %d sessions", thread_index,
321			   pool_elts (pool));
322	  continue;
323	}
324
325      if (pool_elts (pool) > 50)
326	{
327	  vlib_cli_output (vm, "Thread %u: %d sessions. Verbose output "
328			   "suppressed. For more details use filters.",
329			   thread_index, pool_elts (pool));
330	  continue;
331	}
332
333      if (verbose == 1)
334	vlib_cli_output (vm, "%s%-50s%-15s%-10s%-10s",
335			 thread_index ? "\n" : "",
336			 "Connection", "State", "Rx-f", "Tx-f");
337
338      n_closed = 0;
339
340      /* *INDENT-OFF* */
341      pool_foreach(s, pool, ({
342        if (s->session_state >= SESSION_STATE_TRANSPORT_DELETED)
343          {
344            n_closed += 1;
345            continue;
346          }
347        vlib_cli_output (vm, "%U", format_session, s, verbose);
348      }));
349      /* *INDENT-ON* */
350
351      if (!n_closed)
352	vlib_cli_output (vm, "Thread %d: active sessions %u", thread_index,
353			 pool_elts (pool) - n_closed);
354      else
355	vlib_cli_output (vm, "Thread %d: active sessions %u closed %u",
356			 thread_index, pool_elts (pool) - n_closed, n_closed);
357    }
358}
359
360static int
361session_cli_filter_check (session_t * s, session_state_t * states,
362			  transport_proto_t tp)
363{
364  if (states)
365    {
366      session_state_t *state;
367      vec_foreach (state, states) if (s->session_state == *state)
368	goto check_transport;
369      return 0;
370    }
371
372check_transport:
373
374  if (tp != TRANSPORT_N_PROTO && session_get_transport_proto (s) != tp)
375    return 0;
376
377  return 1;
378}
379
380static void
381session_cli_show_session_filter (vlib_main_t * vm, u32 thread_index,
382				 u32 start, u32 end, session_state_t * states,
383				 transport_proto_t tp, int verbose)
384{
385  u8 output_suppressed = 0;
386  session_worker_t *wrk;
387  session_t *pool, *s;
388  u32 count = 0, max_index;
389  int i;
390
391  wrk = session_main_get_worker_if_valid (thread_index);
392  if (!wrk)
393    {
394      vlib_cli_output (vm, "invalid thread index %u", thread_index);
395      return;
396    }
397
398  pool = wrk->sessions;
399
400  if (tp == TRANSPORT_N_PROTO && states == 0 && !verbose
401      && (start == 0 && end == ~0))
402    {
403      vlib_cli_output (vm, "Thread %d: %u sessions", thread_index,
404		       pool_elts (pool));
405      return;
406    }
407
408  max_index = pool_len (pool) ? pool_len (pool) - 1 : 0;
409  for (i = start; i <= clib_min (end, max_index); i++)
410    {
411      if (pool_is_free_index (pool, i))
412	continue;
413
414      s = pool_elt_at_index (pool, i);
415
416      if (session_cli_filter_check (s, states, tp))
417	{
418	  count += 1;
419	  if (verbose)
420	    {
421	      if (count > 50 || (verbose > 1 && count > 10))
422		{
423		  output_suppressed = 1;
424		  continue;
425		}
426	      if (s->session_state < SESSION_STATE_TRANSPORT_DELETED)
427		vlib_cli_output (vm, "%U", format_session, s, verbose);
428	    }
429	}
430    }
431
432  if (!output_suppressed)
433    vlib_cli_output (vm, "Thread %d: %u sessions matched filter",
434		     thread_index, count);
435  else
436    vlib_cli_output (vm, "Thread %d: %u sessions matched filter. Not all"
437		     " shown. Use finer grained filter.", thread_index,
438		     count);
439}
440
441static void
442session_cli_print_transport_protos (vlib_main_t * vm)
443{
444#define _(sym, str, sstr) vlib_cli_output (vm, str);
445  foreach_transport_proto
446#undef _
447}
448
449static void
450session_cli_print_session_states (vlib_main_t * vm)
451{
452#define _(sym, str) vlib_cli_output (vm, str);
453  foreach_session_state
454#undef _
455}
456
457static clib_error_t *
458show_session_command_fn (vlib_main_t * vm, unformat_input_t * input,
459			 vlib_cli_command_t * cmd)
460{
461  u8 one_session = 0, do_listeners = 0, sst, do_elog = 0, do_filter = 0;
462  u32 track_index, thread_index = 0, start = 0, end = ~0, session_index;
463  unformat_input_t _line_input, *line_input = &_line_input;
464  transport_proto_t transport_proto = TRANSPORT_N_PROTO;
465  session_state_t state = SESSION_N_STATES, *states = 0;
466  session_main_t *smm = &session_main;
467  clib_error_t *error = 0;
468  app_worker_t *app_wrk;
469  u32 transport_index;
470  const u8 *app_name;
471  int verbose = 0;
472  session_t *s;
473
474  session_cli_return_if_not_enabled ();
475
476  if (!unformat_user (input, unformat_line_input, line_input))
477    {
478      session_cli_show_all_sessions (vm, 0);
479      return 0;
480    }
481
482  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
483    {
484      if (unformat (line_input, "verbose %d", &verbose))
485	;
486      else if (unformat (line_input, "verbose"))
487	verbose = 1;
488      else if (unformat (line_input, "listeners %U", unformat_transport_proto,
489			 &transport_proto))
490	do_listeners = 1;
491      else if (unformat (line_input, "%U", unformat_session, &s))
492	{
493	  one_session = 1;
494	}
495      else if (unformat (line_input, "thread %u index %u", &thread_index,
496			 &session_index))
497	{
498	  s = session_get_if_valid (session_index, thread_index);
499	  if (!s)
500	    {
501	      vlib_cli_output (vm, "session is not allocated");
502	      goto done;
503	    }
504	  one_session = 1;
505	}
506      else if (unformat (line_input, "thread %u", &thread_index))
507	{
508	  do_filter = 1;
509	}
510      else
511	if (unformat (line_input, "state %U", unformat_session_state, &state))
512	{
513	  vec_add1 (states, state);
514	  do_filter = 1;
515	}
516      else if (unformat (line_input, "proto %U index %u",
517			 unformat_transport_proto, &transport_proto,
518			 &transport_index))
519	{
520	  transport_connection_t *tc;
521	  tc = transport_get_connection (transport_proto, transport_index,
522					 thread_index);
523	  if (!tc)
524	    {
525	      vlib_cli_output (vm, "transport connection %u thread %u is not"
526			       " allocated", transport_index, thread_index);
527	      goto done;
528	    }
529	  s = session_get_if_valid (tc->s_index, thread_index);
530	  if (!s)
531	    {
532	      vlib_cli_output (vm, "session for transport connection %u "
533			       "thread %u does not exist", transport_index,
534			       thread_index);
535	      goto done;
536	    }
537	  one_session = 1;
538	}
539      else if (unformat (line_input, "proto %U", unformat_transport_proto,
540			 &transport_proto))
541	do_filter = 1;
542      else if (unformat (line_input, "range %u %u", &start, &end))
543	do_filter = 1;
544      else if (unformat (line_input, "range %u", &start))
545	{
546	  end = start + 50;
547	  do_filter = 1;
548	}
549      else if (unformat (line_input, "elog"))
550	do_elog = 1;
551      else if (unformat (line_input, "protos"))
552	{
553	  session_cli_print_transport_protos (vm);
554	  goto done;
555	}
556      else if (unformat (line_input, "states"))
557	{
558	  session_cli_print_session_states (vm);
559	  goto done;
560	}
561      else
562	{
563	  error = clib_error_return (0, "unknown input `%U'",
564				     format_unformat_error, line_input);
565	  goto done;
566	}
567    }
568
569  if (one_session)
570    {
571      u8 *str = format (0, "%U", format_session, s, 3);
572      if (do_elog && s->session_state != SESSION_STATE_LISTENING)
573	{
574	  elog_main_t *em = &vm->elog_main;
575	  transport_connection_t *tc;
576	  f64 dt;
577
578	  tc = session_get_transport (s);
579	  track_index = transport_elog_track_index (tc);
580	  dt = (em->init_time.cpu - vm->clib_time.init_cpu_time)
581	    * vm->clib_time.seconds_per_clock;
582	  if (track_index != ~0)
583	    str = format (str, " session elog:\n%U", format_elog_track, em,
584			  dt, track_index);
585	}
586      vlib_cli_output (vm, "%v", str);
587      vec_free (str);
588      goto done;
589    }
590
591  if (do_listeners)
592    {
593      sst = session_type_from_proto_and_ip (transport_proto, 1);
594      vlib_cli_output (vm, "%-50s%-24s", "Listener", "App");
595      /* *INDENT-OFF* */
596      pool_foreach (s, smm->wrk[0].sessions, ({
597	if (s->session_state != SESSION_STATE_LISTENING
598	    || s->session_type != sst)
599	  continue;
600	app_wrk = app_worker_get (s->app_wrk_index);
601	app_name = application_name_from_index (app_wrk->app_index);
602	vlib_cli_output (vm, "%U%-25v%", format_session, s, 0,
603			 app_name);
604      }));
605      /* *INDENT-ON* */
606      goto done;
607    }
608
609  if (do_filter)
610    {
611      if (end < start)
612	{
613	  error = clib_error_return (0, "invalid range start: %u end: %u",
614				     start, end);
615	  goto done;
616	}
617      session_cli_show_session_filter (vm, thread_index, start, end, states,
618				       transport_proto, verbose);
619      goto done;
620    }
621
622  session_cli_show_all_sessions (vm, verbose);
623
624done:
625  unformat_free (line_input);
626  vec_free (states);
627  return error;
628}
629
630/* *INDENT-OFF* */
631VLIB_CLI_COMMAND (vlib_cli_show_session_command) =
632{
633  .path = "show session",
634  .short_help = "show session [verbose [n]] [listeners <proto>] "
635		"[<session-id> [elog]] [thread <n> [index <n>] "
636		"[proto <proto>] [state <state>] [range <min> [<max>]] "
637		"[protos] [states] ",
638  .function = show_session_command_fn,
639};
640/* *INDENT-ON* */
641
642static int
643clear_session (session_t * s)
644{
645  app_worker_t *server_wrk = app_worker_get (s->app_wrk_index);
646  app_worker_close_notify (server_wrk, s);
647  return 0;
648}
649
650static clib_error_t *
651clear_session_command_fn (vlib_main_t * vm, unformat_input_t * input,
652			  vlib_cli_command_t * cmd)
653{
654  session_main_t *smm = &session_main;
655  u32 thread_index = 0, clear_all = 0;
656  session_worker_t *wrk;
657  u32 session_index = ~0;
658  session_t *session;
659
660  if (!smm->is_enabled)
661    {
662      return clib_error_return (0, "session layer is not enabled");
663    }
664
665  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
666    {
667      if (unformat (input, "thread %d", &thread_index))
668	;
669      else if (unformat (input, "session %d", &session_index))
670	;
671      else if (unformat (input, "all"))
672	clear_all = 1;
673      else
674	return clib_error_return (0, "unknown input `%U'",
675				  format_unformat_error, input);
676    }
677
678  if (!clear_all && session_index == ~0)
679    return clib_error_return (0, "session <nn> required, but not set.");
680
681  if (session_index != ~0)
682    {
683      session = session_get_if_valid (session_index, thread_index);
684      if (!session)
685	return clib_error_return (0, "no session %d on thread %d",
686				  session_index, thread_index);
687      clear_session (session);
688    }
689
690  if (clear_all)
691    {
692      /* *INDENT-OFF* */
693      vec_foreach (wrk, smm->wrk)
694	{
695	  pool_foreach(session, wrk->sessions, ({
696	    clear_session (session);
697	  }));
698	};
699      /* *INDENT-ON* */
700    }
701
702  return 0;
703}
704
705/* *INDENT-OFF* */
706VLIB_CLI_COMMAND (clear_session_command, static) =
707{
708  .path = "clear session",
709  .short_help = "clear session thread <thread> session <index>",
710  .function = clear_session_command_fn,
711};
712/* *INDENT-ON* */
713
714static clib_error_t *
715show_session_fifo_trace_command_fn (vlib_main_t * vm,
716				    unformat_input_t * input,
717				    vlib_cli_command_t * cmd)
718{
719  session_t *s = 0;
720  u8 is_rx = 0, *str = 0;
721
722  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
723    {
724      if (unformat (input, "%U", unformat_session, &s))
725	;
726      else if (unformat (input, "rx"))
727	is_rx = 1;
728      else if (unformat (input, "tx"))
729	is_rx = 0;
730      else
731	return clib_error_return (0, "unknown input `%U'",
732				  format_unformat_error, input);
733    }
734
735  if (!SVM_FIFO_TRACE)
736    {
737      vlib_cli_output (vm, "fifo tracing not enabled");
738      return 0;
739    }
740
741  if (!s)
742    {
743      vlib_cli_output (vm, "could not find session");
744      return 0;
745    }
746
747  str = is_rx ?
748    svm_fifo_dump_trace (str, s->rx_fifo) :
749    svm_fifo_dump_trace (str, s->tx_fifo);
750
751  vlib_cli_output (vm, "%v", str);
752  return 0;
753}
754
755/* *INDENT-OFF* */
756VLIB_CLI_COMMAND (show_session_fifo_trace_command, static) =
757{
758  .path = "show session fifo trace",
759  .short_help = "show session fifo trace <session>",
760  .function = show_session_fifo_trace_command_fn,
761};
762/* *INDENT-ON* */
763
764static clib_error_t *
765session_replay_fifo_command_fn (vlib_main_t * vm, unformat_input_t * input,
766				vlib_cli_command_t * cmd)
767{
768  session_t *s = 0;
769  u8 is_rx = 0, *str = 0;
770
771  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
772    {
773      if (unformat (input, "%U", unformat_session, &s))
774	;
775      else if (unformat (input, "rx"))
776	is_rx = 1;
777      else
778	return clib_error_return (0, "unknown input `%U'",
779				  format_unformat_error, input);
780    }
781
782  if (!SVM_FIFO_TRACE)
783    {
784      vlib_cli_output (vm, "fifo tracing not enabled");
785      return 0;
786    }
787
788  if (!s)
789    {
790      vlib_cli_output (vm, "could not find session");
791      return 0;
792    }
793
794  str = is_rx ?
795    svm_fifo_replay (str, s->rx_fifo, 0, 1) :
796    svm_fifo_replay (str, s->tx_fifo, 0, 1);
797
798  vlib_cli_output (vm, "%v", str);
799  return 0;
800}
801
802/* *INDENT-OFF* */
803VLIB_CLI_COMMAND (session_replay_fifo_trace_command, static) =
804{
805  .path = "session replay fifo",
806  .short_help = "session replay fifo <session>",
807  .function = session_replay_fifo_command_fn,
808};
809/* *INDENT-ON* */
810
811static clib_error_t *
812session_enable_disable_fn (vlib_main_t * vm, unformat_input_t * input,
813			   vlib_cli_command_t * cmd)
814{
815  unformat_input_t _line_input, *line_input = &_line_input;
816  u8 is_en = 1;
817  clib_error_t *error;
818
819  if (!unformat_user (input, unformat_line_input, line_input))
820    return clib_error_return (0, "expected enable | disable");
821
822  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
823    {
824      if (unformat (line_input, "enable"))
825	is_en = 1;
826      else if (unformat (line_input, "disable"))
827	is_en = 0;
828      else
829	{
830	  error = clib_error_return (0, "unknown input `%U'",
831				     format_unformat_error, line_input);
832	  unformat_free (line_input);
833	  return error;
834	}
835    }
836
837  unformat_free (line_input);
838  return vnet_session_enable_disable (vm, is_en);
839}
840
841/* *INDENT-OFF* */
842VLIB_CLI_COMMAND (session_enable_disable_command, static) =
843{
844  .path = "session",
845  .short_help = "session [enable|disable]",
846  .function = session_enable_disable_fn,
847};
848/* *INDENT-ON* */
849
850/*
851 * fd.io coding-style-patch-verification: ON
852 *
853 * Local Variables:
854 * eval: (c-set-style "gnu")
855 * End:
856 */
857