ip: add ip_table_allocate to api

Set tableID = ~0 for auto selection unused ID

https://jira.fd.io/browse/VPP-1993

Type: improvement
Change-Id: I4eec2cc1d18fc025196cb6ac4c9a4b374388eb56
Signed-off-by: Artem Glazychev <artem.glazychev@xored.com>
Signed-off-by: Aloys Augustin <aloaugus@cisco.com>
diff --git a/src/vnet/fib/fib_table.c b/src/vnet/fib/fib_table.c
index 7cc989d..f222828 100644
--- a/src/vnet/fib/fib_table.c
+++ b/src/vnet/fib/fib_table.c
@@ -1322,6 +1322,7 @@
 {
     vec_validate(fib_table->ft_locks, source);
 
+    ASSERT(fib_table->ft_locks[source] > 0);
     fib_table->ft_locks[source]--;
     fib_table->ft_total_locks--;
 }
@@ -1350,7 +1351,7 @@
     if (0 == fib_table->ft_total_locks)
     {
         /*
-         * no more locak from any source - kill it
+         * no more lock from any source - kill it
          */
 	fib_table_destroy(fib_table);
     }
diff --git a/src/vnet/ip/ip.api b/src/vnet/ip/ip.api
index 28786fa..ca1e200 100644
--- a/src/vnet/ip/ip.api
+++ b/src/vnet/ip/ip.api
@@ -20,7 +20,7 @@
     called through a shared memory interface.
 */
 
-option version = "3.1.0";
+option version = "3.2.0";
 
 import "vnet/interface_types.api";
 import "vnet/fib/fib_types.api";
@@ -57,6 +57,35 @@
   vl_api_ip_table_t table;
 };
 
+/** \brief Allocate an unused table
+           A table can be added multiple times.
+           If a large number of tables are in use (millions), this API might
+           fail to find a free ID with very low probability, and will return
+           EAGAIN. A subsequent attempt may be successful.
+  @param client_index - opaque cookie to identify the sender
+  @param context - sender context, to match reply w/ request
+  @param table - if table.table_id == ~0, vpp allocates an unused table_id and
+                    proceeds as in ip_table_add_del with is_add = true
+                 if table.table_id != ~0, vpp uses the table.table_id and
+                    proceeds as in ip_table_add_del with is_add = true
+                 table.table_id should never be 0
+*/
+define ip_table_allocate
+{
+  u32 client_index;
+  u32 context;
+
+  vl_api_ip_table_t table;
+};
+
+define ip_table_allocate_reply
+{
+  u32 context;
+  i32 retval;
+
+  vl_api_ip_table_t table;
+};
+
 /** \brief Dump IP all fib tables
     @param client_index - opaque cookie to identify the sender
     @param context - sender context, to match reply w/ request
diff --git a/src/vnet/ip/ip.h b/src/vnet/ip/ip.h
index 6d822d2..cda0de2 100644
--- a/src/vnet/ip/ip.h
+++ b/src/vnet/ip/ip.h
@@ -270,6 +270,8 @@
 int ip_table_bind (fib_protocol_t fproto, u32 sw_if_index,
 		   u32 table_id, u8 is_api);
 
+u32 ip_table_get_unused_id (fib_protocol_t fproto);
+
 u8 ip_is_zero (ip46_address_t * ip46_address, u8 is_ip4);
 u8 ip_is_local_host (ip46_address_t * ip46_address, u8 is_ip4);
 u8 ip4_is_local_host (ip4_address_t * ip4_address);
diff --git a/src/vnet/ip/ip_api.c b/src/vnet/ip/ip_api.c
index f9f9ac7..f5ebd02 100644
--- a/src/vnet/ip/ip_api.c
+++ b/src/vnet/ip/ip_api.c
@@ -601,6 +601,32 @@
     }
 }
 
+/*
+ * Returns an unused table id, and ~0 if it can't find one.
+ */
+u32
+ip_table_get_unused_id (fib_protocol_t fproto)
+{
+  int i, j;
+  u32 seed = random_default_seed ();
+  /* limit to 1M tries */
+  for (j = 0; j < 1 << 10; j++)
+    {
+      seed = random_u32 (&seed);
+      for (i = 0; i < 1 << 10; i++)
+	{
+	  /* look around randomly generated id */
+	  seed += (2 * (i % 2) - 1) * i;
+	  if (seed == ~0)
+	    continue;
+	  if (fib_table_find (fproto, seed) == ~0)
+	    return seed;
+	}
+    }
+
+  return ~0;
+}
+
 void
 vl_api_ip_table_add_del_t_handler (vl_api_ip_table_add_del_t * mp)
 {
@@ -622,6 +648,29 @@
   REPLY_MACRO (VL_API_IP_TABLE_ADD_DEL_REPLY);
 }
 
+void
+vl_api_ip_table_allocate_t_handler (vl_api_ip_table_allocate_t *mp)
+{
+  vl_api_ip_table_allocate_reply_t *rmp;
+  fib_protocol_t fproto =
+    (mp->table.is_ip6 ? FIB_PROTOCOL_IP6 : FIB_PROTOCOL_IP4);
+  u32 table_id = ntohl (mp->table.table_id);
+  int rv = 0;
+
+  if (~0 == table_id)
+    table_id = ip_table_get_unused_id (fproto);
+
+  if (~0 == table_id)
+    rv = VNET_API_ERROR_EAGAIN;
+  else
+    ip_table_create (fproto, table_id, 1, mp->table.name);
+
+  REPLY_MACRO2 (VL_API_IP_TABLE_ALLOCATE_REPLY, {
+    clib_memcpy_fast (&rmp->table, &mp->table, sizeof (mp->table));
+    rmp->table.table_id = htonl (table_id);
+  })
+}
+
 static int
 ip_route_add_del_t_handler (vl_api_ip_route_add_del_t * mp, u32 * stats_index)
 {
diff --git a/src/vnet/ip/ip_test.c b/src/vnet/ip/ip_test.c
index 2de8235..6bdeca5 100644
--- a/src/vnet/ip/ip_test.c
+++ b/src/vnet/ip/ip_test.c
@@ -524,6 +524,17 @@
   return ret;
 }
 
+static int
+api_ip_table_allocate (vat_main_t *vam)
+{
+  return -1;
+}
+
+static void
+vl_api_ip_table_allocate_reply_t_handler (vl_api_ip_table_allocate_reply_t *mp)
+{
+}
+
 static void
 vl_api_ip_route_add_del_v2_reply_t_handler (
   vl_api_ip_route_add_del_v2_reply_t *mp)
diff --git a/src/vnet/ip/lookup.c b/src/vnet/ip/lookup.c
index 1753ffd..5db14ba 100644
--- a/src/vnet/ip/lookup.c
+++ b/src/vnet/ip/lookup.c
@@ -399,27 +399,32 @@
 	}
     }
 
-  if (~0 == table_id)
-    {
-      error = clib_error_return (0, "No table id");
-      goto done;
-    }
-  else if (0 == table_id)
+  if (0 == table_id)
     {
       error = clib_error_return (0, "Can't change the default table");
       goto done;
     }
   else
-    {
-      if (is_add)
 	{
-	  ip_table_create (fproto, table_id, 0, name);
+	  if (is_add)
+	    {
+	      if (~0 == table_id)
+		{
+		  table_id = ip_table_get_unused_id (fproto);
+		  vlib_cli_output (vm, "%u\n", table_id);
+		}
+	      ip_table_create (fproto, table_id, 0, name);
+	    }
+	  else
+	    {
+	      if (~0 == table_id)
+		{
+		  error = clib_error_return (0, "No table id");
+		  goto done;
+		}
+	      ip_table_delete (fproto, table_id, 0);
+	    }
 	}
-      else
-	{
-	  ip_table_delete (fproto, table_id, 0);
-	}
-    }
 
 done:
   unformat_free (line_input);