session: support for cl port reuse

Adds support for connectionless listener port reuse. Until now, cl
listeners had fifos allocated to them and therefore only one app worker
could ever listen, i.e., a session cannot have multiple fifos.

To circumvent the limitation, this separates the fifos from the listener
by allocating new cl sessions for each app worker that reuses the app
listener. Flows are hashed to app worker cl sessions but, for now, this
is not a consistent/fixed hash.

Type: improvement

Signed-off-by: Florin Coras <fcoras@cisco.com>
Change-Id: Ic6533cd47f2765903669f88c288bd592fb17a19e
diff --git a/src/vnet/session/application.c b/src/vnet/session/application.c
index fdd5a0a..5c8efe1 100644
--- a/src/vnet/session/application.c
+++ b/src/vnet/session/application.c
@@ -52,6 +52,7 @@
 app_listener_free (application_t * app, app_listener_t * app_listener)
 {
   clib_bitmap_free (app_listener->workers);
+  vec_free (app_listener->cl_listeners);
   if (CLIB_DEBUG)
     clib_memset (app_listener, 0xfa, sizeof (*app_listener));
   pool_put (app->listeners, app_listener);
@@ -321,6 +322,13 @@
   return listen_session_get (al->local_index);
 }
 
+session_t *
+app_listener_get_wrk_cl_session (app_listener_t *al, u32 wrk_map_index)
+{
+  u32 si = vec_elt (al->cl_listeners, wrk_map_index);
+  return session_get (si, 0 /* listener thread */);
+}
+
 static app_worker_map_t *
 app_worker_map_alloc (application_t * app)
 {
@@ -1017,6 +1025,53 @@
   return app_listener_select_worker (app, al);
 }
 
+always_inline u32
+app_listener_cl_flow_hash (session_dgram_hdr_t *hdr)
+{
+  u32 hash = 0;
+
+  if (hdr->is_ip4)
+    {
+      hash = clib_crc32c_u32 (hash, hdr->rmt_ip.ip4.as_u32);
+      hash = clib_crc32c_u32 (hash, hdr->lcl_ip.ip4.as_u32);
+      hash = clib_crc32c_u16 (hash, hdr->rmt_port);
+      hash = clib_crc32c_u16 (hash, hdr->lcl_port);
+    }
+  else
+    {
+      hash = clib_crc32c_u64 (hash, hdr->rmt_ip.ip6.as_u64[0]);
+      hash = clib_crc32c_u64 (hash, hdr->rmt_ip.ip6.as_u64[1]);
+      hash = clib_crc32c_u64 (hash, hdr->lcl_ip.ip6.as_u64[0]);
+      hash = clib_crc32c_u64 (hash, hdr->lcl_ip.ip6.as_u64[1]);
+      hash = clib_crc32c_u16 (hash, hdr->rmt_port);
+      hash = clib_crc32c_u16 (hash, hdr->lcl_port);
+    }
+
+  return hash;
+}
+
+session_t *
+app_listener_select_wrk_cl_session (session_t *ls, session_dgram_hdr_t *hdr)
+{
+  u32 wrk_map_index = 0;
+  application_t *app;
+  app_listener_t *al;
+
+  app = application_get (ls->app_index);
+  al = app_listener_get (app, ls->al_index);
+  /* Crude test to check if only worker 0 is set */
+  if (al->workers[0] != 1)
+    {
+      u32 hash = app_listener_cl_flow_hash (hdr);
+      hash %= vec_len (al->workers) * sizeof (uword);
+      wrk_map_index = clib_bitmap_next_set (al->workers, hash);
+      if (wrk_map_index == ~0)
+	wrk_map_index = clib_bitmap_first_set (al->workers);
+    }
+
+  return app_listener_get_wrk_cl_session (al, wrk_map_index);
+}
+
 int
 application_alloc_worker_and_init (application_t * app, app_worker_t ** wrk)
 {