fib: make mfib optional

In some cases we do not need multicast support. Making it optional helps
scaling to high number of VRFs, by reducing the control plane operations
and memory consumption.

Type: improvement

Change-Id: Ib34ed3fe2806e2f4624981da4e4a3c49c69f70be
Signed-off-by: Benoît Ganne <bganne@cisco.com>
diff --git a/src/vnet/ip/ip.api b/src/vnet/ip/ip.api
index 967f56c..fc7d758 100644
--- a/src/vnet/ip/ip.api
+++ b/src/vnet/ip/ip.api
@@ -57,6 +57,23 @@
   vl_api_ip_table_t table;
 };
 
+/** \brief Add / del table request - version 2
+           A table can be added multiple times, but need be deleted only once.
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param table - the FIB table to add or del
+    @param create_mfib - whether to create mfib or not
+    @param is_add - add or del
+*/
+autoreply define ip_table_add_del_v2
+{
+  u32 client_index;
+  u32 context;
+  vl_api_ip_table_t table;
+  bool create_mfib [default=true];
+  bool is_add [default=true];
+};
+
 /** \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
diff --git a/src/vnet/ip/ip.h b/src/vnet/ip/ip.h
index 9ebefa0..084243d 100644
--- a/src/vnet/ip/ip.h
+++ b/src/vnet/ip/ip.h
@@ -262,7 +262,7 @@
 extern vlib_node_registration_t ip6_inacl_node;
 
 void ip_table_create (fib_protocol_t fproto, u32 table_id, u8 is_api,
-		      const u8 * name);
+		      u8 create_mfib, const u8 *name);
 
 void ip_table_delete (fib_protocol_t fproto, u32 table_id, u8 is_api);
 
diff --git a/src/vnet/ip/ip_api.c b/src/vnet/ip/ip_api.c
index 6dd9514..5ced88f 100644
--- a/src/vnet/ip/ip_api.c
+++ b/src/vnet/ip/ip_api.c
@@ -636,7 +636,8 @@
 
   if (mp->is_add)
     {
-      ip_table_create (fproto, table_id, 1, mp->table.name);
+      ip_table_create (fproto, table_id, 1 /* is_api */, 1 /* create_mfib */,
+		       mp->table.name);
     }
   else
     {
@@ -647,6 +648,28 @@
 }
 
 void
+vl_api_ip_table_add_del_v2_t_handler (vl_api_ip_table_add_del_v2_t *mp)
+{
+  vl_api_ip_table_add_del_v2_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 (mp->is_add)
+    {
+      ip_table_create (fproto, table_id, 1 /* is_api */, mp->create_mfib,
+		       mp->table.name);
+    }
+  else
+    {
+      ip_table_delete (fproto, table_id, 1);
+    }
+
+  REPLY_MACRO (VL_API_IP_TABLE_ADD_DEL_V2_REPLY);
+}
+
+void
 vl_api_ip_table_allocate_t_handler (vl_api_ip_table_allocate_t *mp)
 {
   vl_api_ip_table_allocate_reply_t *rmp;
@@ -661,7 +684,8 @@
   if (~0 == table_id)
     rv = VNET_API_ERROR_EAGAIN;
   else
-    ip_table_create (fproto, table_id, 1, mp->table.name);
+    ip_table_create (fproto, table_id, 1 /* is_api */, 1 /* create_mfib */,
+		     mp->table.name);
 
   REPLY_MACRO2 (VL_API_IP_TABLE_ALLOCATE_REPLY, {
     clib_memcpy_fast (&rmp->table, &mp->table, sizeof (mp->table));
@@ -915,8 +939,8 @@
 }
 
 void
-ip_table_create (fib_protocol_t fproto,
-		 u32 table_id, u8 is_api, const u8 * name)
+ip_table_create (fib_protocol_t fproto, u32 table_id, u8 is_api,
+		 u8 create_mfib, const u8 *name)
 {
   u32 fib_index, mfib_index;
   vnet_main_t *vnm = vnet_get_main ();
@@ -936,16 +960,23 @@
        * their own unicast tables.
        */
       fib_index = fib_table_find (fproto, table_id);
-      mfib_index = mfib_table_find (fproto, table_id);
-
       /*
        * Always try to re-lock in case the fib was deleted by an API call
        * but was not yet freed because some other locks were held
        */
       fib_table_find_or_create_and_lock_w_name (
 	fproto, table_id, (is_api ? FIB_SOURCE_API : FIB_SOURCE_CLI), name);
-      mfib_table_find_or_create_and_lock_w_name (
-	fproto, table_id, (is_api ? MFIB_SOURCE_API : MFIB_SOURCE_CLI), name);
+
+      if (create_mfib)
+	{
+	  /* same for mfib, if needs be */
+	  mfib_index = mfib_table_find (fproto, table_id);
+	  mfib_table_find_or_create_and_lock_w_name (
+	    fproto, table_id, (is_api ? MFIB_SOURCE_API : MFIB_SOURCE_CLI),
+	    name);
+	}
+      else
+	mfib_index = 0;
 
       if ((~0 == fib_index) || (~0 == mfib_index))
 	call_elf_section_ip_table_callbacks (vnm, table_id, 1 /* is_add */ ,
@@ -1655,9 +1686,10 @@
     rv = VNET_API_ERROR_NO_SUCH_FIB;
   else
     {
+      u32 mfib_index = mfib_table_find (fproto, ntohl (mp->table.table_id));
       fib_table_mark (fib_index, fproto, FIB_SOURCE_API);
-      mfib_table_mark (mfib_table_find (fproto, ntohl (mp->table.table_id)),
-		       fproto, MFIB_SOURCE_API);
+      if (mfib_index != INDEX_INVALID)
+	mfib_table_mark (mfib_index, fproto, MFIB_SOURCE_API);
     }
   REPLY_MACRO (VL_API_IP_TABLE_REPLACE_BEGIN_REPLY);
 }
@@ -1677,10 +1709,10 @@
     rv = VNET_API_ERROR_NO_SUCH_FIB;
   else
     {
+      u32 mfib_index = mfib_table_find (fproto, ntohl (mp->table.table_id));
       fib_table_sweep (fib_index, fproto, FIB_SOURCE_API);
-      mfib_table_sweep (mfib_table_find
-			(fproto, ntohl (mp->table.table_id)), fproto,
-			MFIB_SOURCE_API);
+      if (mfib_index != INDEX_INVALID)
+	mfib_table_sweep (mfib_index, fproto, MFIB_SOURCE_API);
     }
   REPLY_MACRO (VL_API_IP_TABLE_REPLACE_END_REPLY);
 }
@@ -1703,6 +1735,7 @@
       vnet_main_t *vnm = vnet_get_main ();
       vnet_interface_main_t *im = &vnm->interface_main;
       vnet_sw_interface_t *si;
+      u32 mfib_index;
 
       /* Shut down interfaces in this FIB / clean out intfc routes */
       pool_foreach (si, im->sw_interfaces)
@@ -1717,8 +1750,10 @@
       }
 
       fib_table_flush (fib_index, fproto, FIB_SOURCE_API);
-      mfib_table_flush (mfib_table_find (fproto, ntohl (mp->table.table_id)),
-			fproto, MFIB_SOURCE_API);
+
+      mfib_index = mfib_table_find (fproto, ntohl (mp->table.table_id));
+      if (mfib_index != INDEX_INVALID)
+	mfib_table_flush (mfib_index, fproto, MFIB_SOURCE_API);
     }
 
   REPLY_MACRO (VL_API_IP_TABLE_FLUSH_REPLY);
diff --git a/src/vnet/ip/ip_test.c b/src/vnet/ip/ip_test.c
index 727afba..0d1c710 100644
--- a/src/vnet/ip/ip_test.c
+++ b/src/vnet/ip/ip_test.c
@@ -464,6 +464,60 @@
 }
 
 static int
+api_ip_table_add_del_v2 (vat_main_t *vam)
+{
+  unformat_input_t *i = vam->input;
+  vl_api_ip_table_add_del_v2_t *mp;
+  u8 create_mfib = 1;
+  u32 table_id = ~0;
+  u8 is_ipv6 = 0;
+  u8 is_add = 1;
+  int ret = 0;
+
+  /* Parse args required to build the message */
+  while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (i, "ipv6"))
+	is_ipv6 = 1;
+      else if (unformat (i, "del"))
+	is_add = 0;
+      else if (unformat (i, "add"))
+	is_add = 1;
+      else if (unformat (i, "table %d", &table_id))
+	;
+      else if (unformat (i, "no-mfib"))
+	create_mfib = 0;
+      else
+	{
+	  clib_warning ("parse error '%U'", format_unformat_error, i);
+	  return -99;
+	}
+    }
+
+  if (~0 == table_id)
+    {
+      errmsg ("missing table-ID");
+      return -99;
+    }
+
+  /* Construct the API message */
+  M (IP_TABLE_ADD_DEL_V2, mp);
+
+  mp->table.table_id = ntohl (table_id);
+  mp->table.is_ip6 = is_ipv6;
+  mp->is_add = is_add;
+  mp->create_mfib = create_mfib;
+
+  /* send it... */
+  S (mp);
+
+  /* Wait for a reply... */
+  W (ret);
+
+  return ret;
+}
+
+static int
 api_ip_table_replace_begin (vat_main_t *vam)
 {
   unformat_input_t *i = vam->input;
diff --git a/src/vnet/ip/lookup.c b/src/vnet/ip/lookup.c
index c0fa430..b978bd7 100644
--- a/src/vnet/ip/lookup.c
+++ b/src/vnet/ip/lookup.c
@@ -419,10 +419,12 @@
   unformat_input_t _line_input, *line_input = &_line_input;
   clib_error_t *error = NULL;
   u32 table_id, is_add;
+  u8 create_mfib;
   u8 *name = NULL;
 
   is_add = 1;
   table_id = ~0;
+  create_mfib = 1;
 
   /* Get a line of input. */
   if (!unformat_user (main_input, unformat_line_input, line_input))
@@ -438,6 +440,8 @@
 	is_add = 1;
       else if (unformat (line_input, "name %s", &name))
 	;
+      else if (unformat (line_input, "no-mfib"))
+	create_mfib = 0;
       else
 	{
 	  error = unformat_parse_error (line_input);
@@ -459,7 +463,8 @@
 		  table_id = ip_table_get_unused_id (fproto);
 		  vlib_cli_output (vm, "%u\n", table_id);
 		}
-	      ip_table_create (fproto, table_id, 0, name);
+	      ip_table_create (fproto, table_id, 0 /* is_api */, create_mfib,
+			       name);
 	    }
 	  else
 	    {