session: add explicit reset api

Type: feature

This can be used to forcefully close a session. It's only available to
builtin applications for now. Transports must support the reset api
otherwise normal close is used.

Change-Id: I5e6d681cbc4c8045385e293e0e9d86fa2bf45849
Signed-off-by: Florin Coras <fcoras@cisco.com>
diff --git a/src/vnet/session/session.c b/src/vnet/session/session.c
index a102825..4529245 100644
--- a/src/vnet/session/session.c
+++ b/src/vnet/session/session.c
@@ -144,7 +144,7 @@
 }
 
 static void
-session_program_transport_close (session_t * s)
+session_program_transport_ctrl_evt (session_t * s, session_evt_type_t evt)
 {
   u32 thread_index = vlib_get_thread_index ();
   session_evt_elt_t *elt;
@@ -158,10 +158,10 @@
       elt = session_evt_alloc_ctrl (wrk);
       clib_memset (&elt->evt, 0, sizeof (session_event_t));
       elt->evt.session_handle = session_handle (s);
-      elt->evt.event_type = SESSION_CTRL_EVT_CLOSE;
+      elt->evt.event_type = evt;
     }
   else
-    session_send_ctrl_evt_to_thread (s, SESSION_CTRL_EVT_CLOSE);
+    session_send_ctrl_evt_to_thread (s, evt);
 }
 
 session_t *
@@ -888,7 +888,7 @@
       s->session_state = SESSION_STATE_CLOSED;
       session_cleanup_notify (s, SESSION_CLEANUP_TRANSPORT);
       svm_fifo_dequeue_drop_all (s->tx_fifo);
-      session_program_transport_close (s);
+      session_program_transport_ctrl_evt (s, SESSION_CTRL_EVT_CLOSE);
       break;
     case SESSION_STATE_TRANSPORT_DELETED:
       break;
@@ -1194,12 +1194,26 @@
        * acknowledge the close */
       if (s->session_state == SESSION_STATE_TRANSPORT_CLOSED
 	  || s->session_state == SESSION_STATE_TRANSPORT_DELETED)
-	session_program_transport_close (s);
+	session_program_transport_ctrl_evt (s, SESSION_CTRL_EVT_CLOSE);
       return;
     }
 
   s->session_state = SESSION_STATE_CLOSING;
-  session_program_transport_close (s);
+  session_program_transport_ctrl_evt (s, SESSION_CTRL_EVT_CLOSE);
+}
+
+/**
+ * Force a close without waiting for data to be flushed
+ */
+void
+session_reset (session_t * s)
+{
+  if (s->session_state >= SESSION_STATE_CLOSING)
+    return;
+  /* Drop all outstanding tx data */
+  svm_fifo_dequeue_drop_all (s->tx_fifo);
+  s->session_state = SESSION_STATE_CLOSING;
+  session_program_transport_ctrl_evt (s, SESSION_CTRL_EVT_RESET);
 }
 
 /**
@@ -1235,6 +1249,26 @@
 }
 
 /**
+ * Force transport close
+ */
+void
+session_transport_reset (session_t * s)
+{
+  if (s->session_state >= SESSION_STATE_APP_CLOSED)
+    {
+      if (s->session_state == SESSION_STATE_TRANSPORT_CLOSED)
+	s->session_state = SESSION_STATE_CLOSED;
+      else if (s->session_state >= SESSION_STATE_TRANSPORT_DELETED)
+	session_free_w_fifos (s);
+      return;
+    }
+
+  s->session_state = SESSION_STATE_APP_CLOSED;
+  transport_reset (session_get_transport_proto (s), s->connection_index,
+		   s->thread_index);
+}
+
+/**
  * Cleanup transport and session state.
  *
  * Notify transport of the cleanup and free the session. This should
diff --git a/src/vnet/session/session.h b/src/vnet/session/session.h
index 5af824a..de44bed 100644
--- a/src/vnet/session/session.h
+++ b/src/vnet/session/session.h
@@ -376,7 +376,9 @@
 int session_listen (session_t * s, session_endpoint_cfg_t * sep);
 int session_stop_listen (session_t * s);
 void session_close (session_t * s);
+void session_reset (session_t * s);
 void session_transport_close (session_t * s);
+void session_transport_reset (session_t * s);
 void session_transport_cleanup (session_t * s);
 int session_send_io_evt_to_thread (svm_fifo_t * f,
 				   session_evt_type_t evt_type);
diff --git a/src/vnet/session/session_node.c b/src/vnet/session/session_node.c
index 5af54a8..1d662a2 100644
--- a/src/vnet/session/session_node.c
+++ b/src/vnet/session/session_node.c
@@ -902,6 +902,12 @@
 	break;
       session_transport_close (s);
       break;
+    case SESSION_CTRL_EVT_RESET:
+      s = session_get_from_handle_if_valid (e->session_handle);
+      if (PREDICT_FALSE (!s))
+	break;
+      session_transport_reset (s);
+      break;
     case SESSION_IO_EVT_BUILTIN_RX:
       s = session_event_get_session (e, thread_index);
       if (PREDICT_FALSE (!s || s->session_state >= SESSION_STATE_CLOSING))
diff --git a/src/vnet/session/transport.c b/src/vnet/session/transport.c
index 22a356e..aa8deac 100644
--- a/src/vnet/session/transport.c
+++ b/src/vnet/session/transport.c
@@ -324,6 +324,15 @@
   tp_vfts[tp].close (conn_index, thread_index);
 }
 
+void
+transport_reset (transport_proto_t tp, u32 conn_index, u8 thread_index)
+{
+  if (tp_vfts[tp].reset)
+    tp_vfts[tp].reset (conn_index, thread_index);
+  else
+    tp_vfts[tp].close (conn_index, thread_index);
+}
+
 u32
 transport_start_listen (transport_proto_t tp, u32 session_index,
 			transport_endpoint_t * tep)
diff --git a/src/vnet/session/transport.h b/src/vnet/session/transport.h
index c4f74eb..cbe3c36 100644
--- a/src/vnet/session/transport.h
+++ b/src/vnet/session/transport.h
@@ -39,6 +39,7 @@
   u32 (*stop_listen) (u32 conn_index);
   int (*connect) (transport_endpoint_cfg_t * rmt);
   void (*close) (u32 conn_index, u32 thread_index);
+  void (*reset) (u32 conn_index, u32 thread_index);
   void (*cleanup) (u32 conn_index, u32 thread_index);
   clib_error_t *(*enable) (vlib_main_t * vm, u8 is_en);
 
@@ -96,6 +97,7 @@
 
 int transport_connect (transport_proto_t tp, transport_endpoint_cfg_t * tep);
 void transport_close (transport_proto_t tp, u32 conn_index, u8 thread_index);
+void transport_reset (transport_proto_t tp, u32 conn_index, u8 thread_index);
 u32 transport_start_listen (transport_proto_t tp, u32 session_index,
 			    transport_endpoint_t * tep);
 u32 transport_stop_listen (transport_proto_t tp, u32 conn_index);
diff --git a/src/vnet/tcp/tcp.c b/src/vnet/tcp/tcp.c
index 44ee8c8..f9cb2a2 100644
--- a/src/vnet/tcp/tcp.c
+++ b/src/vnet/tcp/tcp.c
@@ -477,6 +477,18 @@
   tcp_connection_cleanup (tc);
 }
 
+static void
+tcp_session_reset (u32 conn_index, u32 thread_index)
+{
+  tcp_connection_t *tc;
+  tc = tcp_connection_get (conn_index, thread_index);
+  session_transport_closed_notify (&tc->connection);
+  tcp_send_reset (tc);
+  tcp_connection_timers_reset (tc);
+  tcp_connection_set_state (tc, TCP_STATE_CLOSED);
+  tcp_timer_update (tc, TCP_TIMER_WAITCLOSE, tcp_cfg.cleanup_time);
+}
+
 /**
  * Initialize all connection timers as invalid
  */
@@ -1258,6 +1270,7 @@
   .connect = tcp_session_open,
   .close = tcp_session_close,
   .cleanup = tcp_session_cleanup,
+  .reset = tcp_session_reset,
   .send_mss = tcp_session_send_mss,
   .send_space = tcp_session_send_space,
   .update_time = tcp_update_time,