vppcom: improve listener session handling

Change-Id: I86b2e2c5a655e53a915fbf62ff04ee23c86de234
Signed-off-by: Florin Coras <fcoras@cisco.com>
diff --git a/src/vcl/vppcom.c b/src/vcl/vppcom.c
index 2f62b81..8c2036c 100644
--- a/src/vcl/vppcom.c
+++ b/src/vcl/vppcom.c
@@ -306,6 +306,46 @@
   return VPPCOM_OK;
 }
 
+static inline void
+vppcom_session_table_add_listener (u64 listener_handle, u32 value)
+{
+  /* Session and listener handles have different formats. The latter has
+   * the thread index in the upper 32 bits while the former has the session
+   * type. Knowing that, for listeners we just flip the MSB to 1 */
+  listener_handle |= 1ULL << 63;
+  hash_set (vcm->session_index_by_vpp_handles, listener_handle, value);
+}
+
+static inline session_t *
+vppcom_session_table_lookup_listener (u64 listener_handle)
+{
+  uword *p;
+  u64 handle = listener_handle | (1ULL << 63);
+  p = hash_get (vcm->session_index_by_vpp_handles, handle);
+  if (!p)
+    {
+      clib_warning ("[%d] couldn't find listen session: unknown vpp "
+		    "listener handle %llx", getpid (), listener_handle);
+      return 0;
+    }
+  if (pool_is_free_index (vcm->sessions, p[0]))
+    {
+      if (VPPCOM_DEBUG > 1)
+	clib_warning ("[%d] invalid listen session, sid (%u)", getpid (),
+		      p[0]);
+      return 0;
+    }
+
+  return pool_elt_at_index (vcm->sessions, p[0]);
+}
+
+static inline void
+vppcom_session_table_del_listener (u64 listener_handle)
+{
+  listener_handle |= 1ULL << 63;
+  hash_unset (vcm->session_index_by_vpp_handles, listener_handle);
+}
+
 static int
 vppcom_connect_to_vpp (char *app_name)
 {
@@ -670,12 +710,26 @@
   session_t *session = 0;
   vl_api_disconnect_session_reply_t *rmp;
   uword *p;
-  int rv = 0;
+  int rv = 0, rval;
 
   p = hash_get (vcm->session_index_by_vpp_handles, mp->handle);
+  if (VPPCOM_DEBUG > 0)
+    {
+      if (!p)
+	{
+	  clib_warning ("[%d] request to disconnect invalid handle (%u)!",
+			getpid (), mp->handle);
+	  rv = -11;
+	  goto done;
+	}
+      clib_warning ("[%d] disconnecting handle %u sid (%u)!", getpid (),
+		    mp->handle, p[0]);
+    }
+
+  goto done;
+
   if (p)
     {
-      int rval;
       clib_spinlock_lock (&vcm->sessions_lockp);
       rval = vppcom_session_at_index (p[0], &session);
       if (PREDICT_FALSE (rval))
@@ -696,6 +750,7 @@
       rv = -11;
     }
 
+done:
   rmp = vl_msg_api_alloc (sizeof (*rmp));
   memset (rmp, 0, sizeof (*rmp));
 
@@ -890,8 +945,11 @@
   if (rv == VPPCOM_OK)
     {
       session->vpp_handle = mp->handle;
-      hash_set (vcm->session_index_by_vpp_handles, mp->handle,
-		vcm->bind_session_index);
+      session->lcl_addr.is_ip4 = mp->lcl_is_ip4;
+      clib_memcpy (&session->lcl_addr.ip46, mp->lcl_ip,
+		   sizeof (session->peer_addr.ip46));
+      session->lcl_port = mp->lcl_port;
+      vppcom_session_table_add_listener (mp->handle, vcm->bind_session_index);
       session->state = mp->retval ? STATE_FAILED : STATE_LISTEN;
       vcm->bind_session_index = ~0;
     }
@@ -902,16 +960,17 @@
 vl_api_unbind_sock_reply_t_handler (vl_api_unbind_sock_reply_t * mp)
 {
   session_t *session = 0;
-  int rv;
 
   clib_spinlock_lock (&vcm->sessions_lockp);
-  rv = vppcom_session_at_index (vcm->bind_session_index, &session);
-  if (rv == VPPCOM_OK)
+  session = vppcom_session_table_lookup_listener (vcm->bind_session_index);
+
+  if (session)
     {
       if ((VPPCOM_DEBUG > 1) && (mp->retval))
 	clib_warning ("[%d] unbind failed: %U", getpid (), format_api_error,
 		      ntohl (mp->retval));
 
+      vppcom_session_table_del_listener (vcm->bind_session_index);
       vcm->bind_session_index = ~0;
       session->state = STATE_START;
     }
@@ -1018,16 +1077,24 @@
 vl_api_accept_session_t_handler (vl_api_accept_session_t * mp)
 {
   svm_fifo_t *rx_fifo, *tx_fifo;
-  session_t *session;
+  session_t *session, *listen_session;
   u32 session_index;
-  uword *p;
 
   clib_spinlock_lock (&vcm->sessions_lockp);
   if (!clib_fifo_free_elts (vcm->client_session_index_fifo))
     {
       clib_warning ("[%d] client session queue is full!", getpid ());
-      vppcom_send_accept_session_reply (VNET_API_ERROR_QUEUE_FULL,
-					mp->handle);
+      vppcom_send_accept_session_reply (mp->handle,
+					VNET_API_ERROR_QUEUE_FULL);
+      clib_spinlock_unlock (&vcm->sessions_lockp);
+      return;
+    }
+
+  listen_session = vppcom_session_table_lookup_listener (mp->listener_handle);
+  if (!listen_session)
+    {
+      clib_warning ("[%d] ERROR: couldn't find listen session: unknown vpp "
+		    "listener handle %llx", getpid (), mp->listener_handle);
       clib_spinlock_unlock (&vcm->sessions_lockp);
       return;
     }
@@ -1057,36 +1124,12 @@
 
   /* Add it to lookup table */
   hash_set (vcm->session_index_by_vpp_handles, mp->handle, session_index);
-
-  p = hash_get (vcm->session_index_by_vpp_handles, mp->listener_handle);
-  if (p)
-    {
-      int rval;
-      session_t *listen_session;
-
-      rval = vppcom_session_at_index (p[0], &listen_session);
-      if (PREDICT_FALSE (rval))
-	{
-	  if (VPPCOM_DEBUG > 1)
-	    clib_warning ("[%d] invalid listen session, sid (%u) "
-			  "has been closed!", getpid (), p[0]);
-	}
-      else
-	{
-	  session->lcl_port = listen_session->lcl_port;
-	  session->lcl_addr = listen_session->lcl_addr;
-	}
-      clib_spinlock_unlock (&vcm->sessions_lockp);
-      hash_unset (vcm->session_index_by_vpp_handles, mp->handle);
-    }
-  else
-    {
-      clib_warning ("[%d] couldn't find listen session: unknown vpp "
-		    "listener handle %llx", getpid (), mp->listener_handle);
-    }
+  session->lcl_port = listen_session->lcl_port;
+  session->lcl_addr = listen_session->lcl_addr;
 
   /* TBD: move client_session_index_fifo into listener session */
   clib_fifo_add1 (vcm->client_session_index_fifo, session_index);
+
   clib_spinlock_unlock (&vcm->sessions_lockp);
 
   if (VPPCOM_DEBUG > 1)
@@ -1145,7 +1188,8 @@
       if (VPPCOM_DEBUG > 1)
 	clib_warning ("[%d] client session queue is full!", getpid ());
       clib_spinlock_unlock (&vcm->sessions_lockp);
-      vppcom_send_accept_session_reply (VNET_API_ERROR_QUEUE_FULL, 0);
+      /* TBD: fix handle */
+      vppcom_send_accept_session_reply (0, VNET_API_ERROR_QUEUE_FULL);
       return;
     }
 
@@ -1264,6 +1308,9 @@
     }
   clib_spinlock_unlock (&vcm->sessions_lockp);
 
+  if (vcm->bind_session_index != session_index)
+    clib_warning ("[%d] ERROR: unbinding a not bound listener %u (%u)",
+		  session_index, vcm->bind_session_index);
   vcm->bind_session_index = session_index;
   vppcom_send_unbind_sock (session_index);
   rv = vppcom_wait_for_session_state_change (session_index, STATE_START,
@@ -1298,6 +1345,9 @@
 
   if (!session->is_cut_thru)
     {
+      if (VPPCOM_DEBUG > 1)
+	clib_warning ("[%d] disconnecting sid (%u)", getpid (),
+		      session_index);
       vppcom_send_disconnect (session);
       clib_spinlock_unlock (&vcm->sessions_lockp);
 
@@ -2425,7 +2475,7 @@
   else
     {
       cut_thru_str = " ";
-      vppcom_send_accept_session_reply (0, client_session->vpp_handle);
+      vppcom_send_accept_session_reply (client_session->vpp_handle, 0);
     }
 
   if (VPPCOM_DEBUG > 0)