Debug CLI to magically create / delete a TCP session

The session ends up in established state, and is hand-crafted to look
like it was created by the builtin_server.

This will come in handy for injecting packets into tcp46-established,
along with ancillary debug CLI to adjust connection parameters.

Immediate applications include screwball window cases, out of order
segments, paws checking, and so on and so forth.

Debug CLI script:

loop create
set int ip address loop0 6.0.1.1/8
set int state loop0 up
set ip arp loop0 6.0.1.2 feed.face.babe
test tcp server
test tcp session

packet-generator new {
  name tcp
  limit 1
  node ip4-input
  size 100-100
  interface loop0
  no-recycle
  data {
    TCP: 6.0.1.2 -> 6.0.1.1
    TCP: 11234 -> 1234
    ACK window 2000
    seqnum 0
    acknum 0
    incrementing 100
  }
}

Change-Id: I866c2159376064b7d14f70531022c1fe949258c2
Signed-off-by: Dave Barach <dave@barachs.net>
diff --git a/src/vnet/tcp/tcp_format.c b/src/vnet/tcp/tcp_format.c
index 994ccfd..1ca2f58 100644
--- a/src/vnet/tcp/tcp_format.c
+++ b/src/vnet/tcp/tcp_format.c
@@ -45,7 +45,8 @@
 {
   int flags = va_arg (*args, int);
 
-#define _(f) if (flags & TCP_FLAG_##f) s = format (s, "%s, ", #f);
+  s = format (s, "0x%02x", flags);
+#define _(f) if (flags & TCP_FLAG_##f) s = format (s, " %s", #f);
   foreach_tcp_flag
 #undef _
     return s;
diff --git a/src/vnet/tcp/tcp_input.c b/src/vnet/tcp/tcp_input.c
index 3bd5387..bfe3665 100644
--- a/src/vnet/tcp/tcp_input.c
+++ b/src/vnet/tcp/tcp_input.c
@@ -2376,7 +2376,7 @@
 		  vnet_buffer (b0)->tcp.flags = tc0->state;
 		  clib_warning ("disp error state %U flags %U",
 				format_tcp_state, &state0,
-				format_tcp_flags, flags0);
+				format_tcp_flags, (int) flags0);
 		}
 	    }
 	  else
diff --git a/src/vnet/tcp/tcp_pg.c b/src/vnet/tcp/tcp_pg.c
index dc32404..3be4592 100644
--- a/src/vnet/tcp/tcp_pg.c
+++ b/src/vnet/tcp/tcp_pg.c
@@ -54,21 +54,19 @@
 static void
 tcp_pg_edit_function (pg_main_t * pg,
 		      pg_stream_t * s,
-		      pg_edit_group_t * g,
-		      u32 * packets,
-		      u32 n_packets)
+		      pg_edit_group_t * g, u32 * packets, u32 n_packets)
 {
-  vlib_main_t * vm = vlib_get_main();
+  vlib_main_t *vm = vlib_get_main ();
   u32 ip_offset, tcp_offset;
 
   tcp_offset = g->start_byte_offset;
-  ip_offset = (g-1)->start_byte_offset;
+  ip_offset = (g - 1)->start_byte_offset;
 
   while (n_packets >= 1)
     {
-      vlib_buffer_t * p0;
-      ip4_header_t * ip0;
-      tcp_header_t * tcp0;
+      vlib_buffer_t *p0;
+      ip4_header_t *ip0;
+      tcp_header_t *tcp0;
       ip_csum_t sum0;
       u32 tcp_len0;
 
@@ -85,7 +83,9 @@
       if (BITS (sum0) == 32)
 	{
 	  sum0 = clib_mem_unaligned (&ip0->src_address, u32);
-	  sum0 = ip_csum_with_carry (sum0, clib_mem_unaligned (&ip0->dst_address, u32));
+	  sum0 =
+	    ip_csum_with_carry (sum0,
+				clib_mem_unaligned (&ip0->dst_address, u32));
 	}
       else
 	sum0 = clib_mem_unaligned (&ip0->src_address, u64);
@@ -96,20 +96,22 @@
       /* Invalidate possibly old checksum. */
       tcp0->checksum = 0;
 
-      sum0 = ip_incremental_checksum_buffer (vm, p0, tcp_offset, tcp_len0, sum0);
+      sum0 =
+	ip_incremental_checksum_buffer (vm, p0, tcp_offset, tcp_len0, sum0);
 
-      tcp0->checksum = ~ ip_csum_fold (sum0);
+      tcp0->checksum = ~ip_csum_fold (sum0);
     }
 }
 
-typedef struct {
+typedef struct
+{
   pg_edit_t src, dst;
   pg_edit_t seq_number, ack_number;
   pg_edit_t data_offset_and_reserved;
 #define _(f) pg_edit_t f##_flag;
-  foreach_tcp_flag
+    foreach_tcp_flag
 #undef _
-  pg_edit_t window;
+    pg_edit_t window;
   pg_edit_t checksum;
   pg_edit_t urgent_pointer;
 } pg_tcp_header_t;
@@ -119,13 +121,13 @@
 {
   /* Initialize fields that are not bit fields in the IP header. */
 #define _(f) pg_edit_init (&p->f, tcp_header_t, f);
-  _ (src);
-  _ (dst);
-  _ (seq_number);
-  _ (ack_number);
-  _ (window);
-  _ (checksum);
-  _ (urgent_pointer);
+  _(src);
+  _(dst);
+  _(seq_number);
+  _(ack_number);
+  _(window);
+  _(checksum);
+  _(urgent_pointer);
 #undef _
 
   /* Initialize bit fields. */
@@ -136,19 +138,17 @@
 
   foreach_tcp_flag
 #undef _
-
-  pg_edit_init_bitfield (&p->data_offset_and_reserved, tcp_header_t,
-			 data_offset_and_reserved,
-			 4, 4);
+    pg_edit_init_bitfield (&p->data_offset_and_reserved, tcp_header_t,
+			   data_offset_and_reserved, 4, 4);
 }
 
 uword
 unformat_pg_tcp_header (unformat_input_t * input, va_list * args)
 {
-  pg_stream_t * s = va_arg (*args, pg_stream_t *);
-  pg_tcp_header_t * p;
+  pg_stream_t *s = va_arg (*args, pg_stream_t *);
+  pg_tcp_header_t *p;
   u32 group_index;
-  
+
   p = pg_create_edit_group (s, sizeof (p[0]), sizeof (tcp_header_t),
 			    &group_index);
   pg_tcp_header_init (p);
@@ -157,8 +157,8 @@
   pg_edit_set_fixed (&p->seq_number, 0);
   pg_edit_set_fixed (&p->ack_number, 0);
 
-  pg_edit_set_fixed (&p->data_offset_and_reserved, 
-                     sizeof (tcp_header_t) / sizeof (u32));
+  pg_edit_set_fixed (&p->data_offset_and_reserved,
+		     sizeof (tcp_header_t) / sizeof (u32));
 
   pg_edit_set_fixed (&p->window, 4096);
   pg_edit_set_fixed (&p->urgent_pointer, 0);
@@ -166,43 +166,44 @@
 #define _(f) pg_edit_set_fixed (&p->f##_flag, 0);
   foreach_tcp_flag
 #undef _
+    p->checksum.type = PG_EDIT_UNSPECIFIED;
 
-  p->checksum.type = PG_EDIT_UNSPECIFIED;
-
-  if (! unformat (input, "TCP: %U -> %U",
-		  unformat_pg_edit,
-		    unformat_tcp_udp_port, &p->src,
-		  unformat_pg_edit,
-		    unformat_tcp_udp_port, &p->dst))
+  if (!unformat (input, "TCP: %U -> %U",
+		 unformat_pg_edit,
+		 unformat_tcp_udp_port, &p->src,
+		 unformat_pg_edit, unformat_tcp_udp_port, &p->dst))
     goto error;
 
   /* Parse options. */
   while (1)
     {
       if (unformat (input, "window %U",
-		    unformat_pg_edit,
-		    unformat_pg_number, &p->window))
+		    unformat_pg_edit, unformat_pg_number, &p->window))
 	;
 
       else if (unformat (input, "checksum %U",
-			 unformat_pg_edit,
-			 unformat_pg_number, &p->checksum))
+			 unformat_pg_edit, unformat_pg_number, &p->checksum))
 	;
 
+      else if (unformat (input, "seqnum %U", unformat_pg_edit,
+			 unformat_pg_number, &p->seq_number))
+	;
+      else if (unformat (input, "acknum %U", unformat_pg_edit,
+			 unformat_pg_number, &p->ack_number))
+	;
       /* Flags. */
 #define _(f) else if (unformat (input, #f)) pg_edit_set_fixed (&p->f##_flag, 1);
-  foreach_tcp_flag
+      foreach_tcp_flag
 #undef _
-
-      /* Can't parse input: try next protocol level. */
-      else
+	/* Can't parse input: try next protocol level. */
+	else
 	break;
     }
 
   {
-    ip_main_t * im = &ip_main;
+    ip_main_t *im = &ip_main;
     u16 dst_port;
-    tcp_udp_port_info_t * pi;
+    tcp_udp_port_info_t *pi;
 
     pi = 0;
     if (p->dst.type == PG_EDIT_FIXED)
@@ -215,12 +216,12 @@
 	&& unformat_user (input, pi->unformat_pg_edit, s))
       ;
 
-    else if (! unformat_user (input, unformat_pg_payload, s))
+    else if (!unformat_user (input, unformat_pg_payload, s))
       goto error;
 
     if (p->checksum.type == PG_EDIT_UNSPECIFIED)
       {
-	pg_edit_group_t * g = pg_stream_get_group (s, group_index);
+	pg_edit_group_t *g = pg_stream_get_group (s, group_index);
 	g->edit_function = tcp_pg_edit_function;
 	g->edit_function_opaque = 0;
       }
@@ -228,9 +229,16 @@
     return 1;
   }
 
- error:
+error:
   /* Free up any edits we may have added. */
   pg_free_edit_group (s);
   return 0;
 }
 
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/tcp/tcp_test.c b/src/vnet/tcp/tcp_test.c
index 0146154..d65ce1b 100644
--- a/src/vnet/tcp/tcp_test.c
+++ b/src/vnet/tcp/tcp_test.c
@@ -895,6 +895,68 @@
   return res;
 }
 
+static int
+tcp_test_session (vlib_main_t * vm, unformat_input_t * input)
+{
+  int rv = 0;
+  tcp_connection_t *tc0;
+  u8 sst = SESSION_TYPE_IP4_TCP;
+  ip4_address_t local, remote;
+  u16 local_port, remote_port;
+  tcp_main_t *tm = vnet_get_tcp_main ();
+  int is_add = 1;
+
+
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (input, "del"))
+	is_add = 0;
+      else if (unformat (input, "add"))
+	is_add = 1;
+      else
+	break;
+    }
+
+  if (is_add)
+    {
+      local.as_u32 = clib_host_to_net_u32 (0x06000101);
+      remote.as_u32 = clib_host_to_net_u32 (0x06000102);
+      local_port = clib_host_to_net_u16 (1234);
+      remote_port = clib_host_to_net_u16 (11234);
+
+      pool_get (tm->connections[0], tc0);
+      memset (tc0, 0, sizeof (*tc0));
+
+      tc0->state = TCP_STATE_ESTABLISHED;
+      tc0->rcv_las = 1;
+      tc0->c_c_index = tc0 - tm->connections[0];
+      tc0->c_lcl_port = local_port;
+      tc0->c_rmt_port = remote_port;
+      tc0->c_is_ip4 = 1;
+      tc0->c_thread_index = 0;
+      tc0->c_lcl_ip4.as_u32 = local.as_u32;
+      tc0->c_rmt_ip4.as_u32 = remote.as_u32;
+      tc0->opt.mss = 1450;
+      tcp_connection_init_vars (tc0);
+
+      TCP_EVT_DBG (TCP_EVT_OPEN, tc0);
+
+      if (stream_session_accept (&tc0->connection, 0 /* listener index */ ,
+				 sst, 0 /* notify */ ))
+	clib_warning ("stream_session_accept failed");
+
+      stream_session_accept_notify (&tc0->connection);
+    }
+  else
+    {
+      tc0 = tcp_connection_get (0 /* connection index */ , 0 /* thread */ );
+      tc0->state = TCP_STATE_CLOSED;
+      stream_session_disconnect_notify (&tc0->connection);
+    }
+
+  return rv;
+}
+
 static clib_error_t *
 tcp_test (vlib_main_t * vm,
 	  unformat_input_t * input, vlib_cli_command_t * cmd_arg)
@@ -911,11 +973,12 @@
 	{
 	  res = tcp_test_fifo (vm, input);
 	}
-      else
+      else if (unformat (input, "session"))
 	{
-	  return clib_error_return (0, "unknown input `%U'",
-				    format_unformat_error, input);
+	  res = tcp_test_session (vm, input);
 	}
+      else
+	break;
     }
 
   if (res)