Implement a loopback instance allocation scheme. 72/5572/3
authorJon Loeliger <jdl@netgate.com>
Thu, 23 Feb 2017 19:57:35 +0000 (13:57 -0600)
committerJohn Lo <loj@cisco.com>
Fri, 3 Mar 2017 23:19:21 +0000 (23:19 +0000)
To support creating loopback interfaces with a specific
instance number, a new CREATE_LOOPBACK_INSTANCE API call
with flag is_specified and value user_instance is introduced.
Presumably the existing CREATE_LOOPBACK API message will be
obsoleted and revmoved.

The VAT cli commands can now mention and format
the new field as 'instance %d' data.  If no instance
number is named, the old call CREATE_LOOPBACK is used
to maintain backward compatibility.  However, if the
instance is named, the new CREATE_LOOPBACK_INSTANCE
message will be used.

Both the dynamically allocated and user-requested instance
number are tracked in a bitvector.  If is_specified is 0,
the next free instance will be used..  A request for a specific
instance number will be granted if it is available.  On error,
the value ~0 is returned.

Change-Id: I849815563a5da736dcd6bccd262ef49b963f6643
Signed-off-by: Jon Loeliger <jdl@netgate.com>
src/vat/api_format.c
src/vnet/ethernet/ethernet.h
src/vnet/ethernet/interface.c
src/vpp/api/api.c
src/vpp/api/custom_dump.c
src/vpp/api/test_client.c
src/vpp/api/vpe.api

index 5243691..993a6e8 100644 (file)
@@ -725,6 +725,34 @@ static void vl_api_create_loopback_reply_t_handler_json
   vam->result_ready = 1;
 }
 
+static void vl_api_create_loopback_instance_reply_t_handler
+  (vl_api_create_loopback_instance_reply_t * mp)
+{
+  vat_main_t *vam = &vat_main;
+  i32 retval = ntohl (mp->retval);
+
+  vam->retval = retval;
+  vam->regenerate_interface_table = 1;
+  vam->sw_if_index = ntohl (mp->sw_if_index);
+  vam->result_ready = 1;
+}
+
+static void vl_api_create_loopback_instance_reply_t_handler_json
+  (vl_api_create_loopback_instance_reply_t * mp)
+{
+  vat_main_t *vam = &vat_main;
+  vat_json_node_t node;
+
+  vat_json_init_object (&node);
+  vat_json_object_add_int (&node, "retval", ntohl (mp->retval));
+  vat_json_object_add_uint (&node, "sw_if_index", ntohl (mp->sw_if_index));
+
+  vat_json_print (vam->ofp, &node);
+  vat_json_free (&node);
+  vam->retval = ntohl (mp->retval);
+  vam->result_ready = 1;
+}
+
 static void vl_api_af_packet_create_reply_t_handler
   (vl_api_af_packet_create_reply_t * mp)
 {
@@ -4010,6 +4038,7 @@ foreach_standard_reply_retval_handler;
 
 #define foreach_vpe_api_reply_msg                                       \
 _(CREATE_LOOPBACK_REPLY, create_loopback_reply)                         \
+_(CREATE_LOOPBACK_INSTANCE_REPLY, create_loopback_instance_reply)       \
 _(SW_INTERFACE_DETAILS, sw_interface_details)                           \
 _(SW_INTERFACE_SET_FLAGS_REPLY, sw_interface_set_flags_reply)           \
 _(CONTROL_PING_REPLY, control_ping_reply)                               \
@@ -4720,8 +4749,11 @@ api_create_loopback (vat_main_t * vam)
 {
   unformat_input_t *i = vam->input;
   vl_api_create_loopback_t *mp;
+  vl_api_create_loopback_instance_t *mp_lbi;
   u8 mac_address[6];
   u8 mac_set = 0;
+  u8 is_specified = 0;
+  u32 user_instance = 0;
   int ret;
 
   memset (mac_address, 0, sizeof (mac_address));
@@ -4730,16 +4762,31 @@ api_create_loopback (vat_main_t * vam)
     {
       if (unformat (i, "mac %U", unformat_ethernet_address, mac_address))
        mac_set = 1;
+      if (unformat (i, "instance %d", &user_instance))
+       is_specified = 1;
       else
        break;
     }
 
-  /* Construct the API message */
-  M (CREATE_LOOPBACK, mp);
-  if (mac_set)
-    clib_memcpy (mp->mac_address, mac_address, sizeof (mac_address));
+  if (is_specified)
+    {
+      M (CREATE_LOOPBACK_INSTANCE, mp_lbi);
+      mp_lbi->is_specified = is_specified;
+      if (is_specified)
+       mp_lbi->user_instance = htonl (user_instance);
+      if (mac_set)
+       clib_memcpy (mp_lbi->mac_address, mac_address, sizeof (mac_address));
+      S (mp_lbi);
+    }
+  else
+    {
+      /* Construct the API message */
+      M (CREATE_LOOPBACK, mp);
+      if (mac_set)
+       clib_memcpy (mp->mac_address, mac_address, sizeof (mac_address));
+      S (mp);
+    }
 
-  S (mp);
   W (ret);
   return ret;
 }
@@ -18021,7 +18068,7 @@ echo (vat_main_t * vam)
 
 /* List of API message constructors, CLI names map to api_xxx */
 #define foreach_vpe_api_msg                                             \
-_(create_loopback,"[mac <mac-addr>]")                                   \
+_(create_loopback,"[mac <mac-addr>] [instance <instance>]")             \
 _(sw_interface_dump,"")                                                 \
 _(sw_interface_set_flags,                                               \
   "<intfc> | sw_if_index <id> admin-up | admin-down link-up | link down") \
index 3acde42..ba84c69 100644 (file)
@@ -265,6 +265,9 @@ typedef struct
 
   /* Feature arc index */
   u8 output_feature_arc_index;
+
+  /* Allocated loopback instances */
+  uword *bm_loopback_instances;
 } ethernet_main_t;
 
 ethernet_main_t ethernet_main;
@@ -412,7 +415,8 @@ clib_error_t *next_by_ethertype_init (next_by_ethertype_t * l3_next);
 clib_error_t *next_by_ethertype_register (next_by_ethertype_t * l3_next,
                                          u32 ethertype, u32 next_index);
 
-int vnet_create_loopback_interface (u32 * sw_if_indexp, u8 * mac_address);
+int vnet_create_loopback_interface (u32 * sw_if_indexp, u8 * mac_address,
+                                   u8 is_specified, u32 user_instance);
 int vnet_delete_loopback_interface (u32 sw_if_index);
 int vnet_delete_sub_interface (u32 sw_if_index);
 
index 9570030..9894e3c 100644 (file)
@@ -453,13 +453,87 @@ VNET_DEVICE_CLASS (ethernet_simulated_device_class) = {
 };
 /* *INDENT-ON* */
 
+
+/*
+ * Maintain a bitmap of allocated loopback instance numbers.
+ */
+#define LOOPBACK_MAX_INSTANCE          (16 * 1024)
+
+static u32
+loopback_instance_alloc (u8 is_specified, u32 want)
+{
+  ethernet_main_t *em = &ethernet_main;
+
+  /*
+   * Check for dynamically allocaetd instance number.
+   */
+  if (!is_specified)
+    {
+      u32 bit;
+
+      bit = clib_bitmap_first_clear (em->bm_loopback_instances);
+      if (bit >= LOOPBACK_MAX_INSTANCE)
+       {
+         return ~0;
+       }
+      em->bm_loopback_instances = clib_bitmap_set (em->bm_loopback_instances,
+                                                  bit, 1);
+      return bit;
+    }
+
+  /*
+   * In range?
+   */
+  if (want >= LOOPBACK_MAX_INSTANCE)
+    {
+      return ~0;
+    }
+
+  /*
+   * Already in use?
+   */
+  if (clib_bitmap_get (em->bm_loopback_instances, want))
+    {
+      return ~0;
+    }
+
+  /*
+   * Grant allocation request.
+   */
+  em->bm_loopback_instances = clib_bitmap_set (em->bm_loopback_instances,
+                                              want, 1);
+
+  return want;
+}
+
+static int
+loopback_instance_free (u32 instance)
+{
+  ethernet_main_t *em = &ethernet_main;
+
+  if (instance >= LOOPBACK_MAX_INSTANCE)
+    {
+      return -1;
+    }
+
+  if (clib_bitmap_get (em->bm_loopback_instances, instance) == 0)
+    {
+      return -1;
+    }
+
+  em->bm_loopback_instances = clib_bitmap_set (em->bm_loopback_instances,
+                                              instance, 0);
+  return 0;
+}
+
 int
-vnet_create_loopback_interface (u32 * sw_if_indexp, u8 * mac_address)
+vnet_create_loopback_interface (u32 * sw_if_indexp, u8 * mac_address,
+                               u8 is_specified, u32 user_instance)
 {
   vnet_main_t *vnm = vnet_get_main ();
   vlib_main_t *vm = vlib_get_main ();
   clib_error_t *error;
-  static u32 instance;
+  u32 instance;
   u8 address[6];
   u32 hw_if_index;
   vnet_hw_interface_t *hw_if;
@@ -472,6 +546,16 @@ vnet_create_loopback_interface (u32 * sw_if_indexp, u8 * mac_address)
 
   memset (address, 0, sizeof (address));
 
+  /*
+   * Allocate a loopback instance.  Either select on dynamically
+   * or try to use the desired user_instance number.
+   */
+  instance = loopback_instance_alloc (is_specified, user_instance);
+  if (instance == ~0)
+    {
+      return VNET_API_ERROR_INVALID_REGISTRATION;
+    }
+
   /*
    * Default MAC address (dead:0000:0000 + instance) is allocated
    * if zero mac_address is configured. Otherwise, user-configurable MAC
@@ -488,7 +572,7 @@ vnet_create_loopback_interface (u32 * sw_if_indexp, u8 * mac_address)
 
   error = ethernet_register_interface
     (vnm,
-     ethernet_simulated_device_class.index, instance++, address, &hw_if_index,
+     ethernet_simulated_device_class.index, instance, address, &hw_if_index,
      /* flag change */ 0);
 
   if (error)
@@ -520,6 +604,8 @@ create_simulated_ethernet_interfaces (vlib_main_t * vm,
   int rv;
   u32 sw_if_index;
   u8 mac_address[6];
+  u8 is_specified = 0;
+  u32 user_instance = 0;
 
   memset (mac_address, 0, sizeof (mac_address));
 
@@ -527,11 +613,14 @@ create_simulated_ethernet_interfaces (vlib_main_t * vm,
     {
       if (unformat (input, "mac %U", unformat_ethernet_address, mac_address))
        ;
+      if (unformat (input, "instance %d", &user_instance))
+       is_specified = 1;
       else
        break;
     }
 
-  rv = vnet_create_loopback_interface (&sw_if_index, mac_address);
+  rv = vnet_create_loopback_interface (&sw_if_index, mac_address,
+                                      is_specified, user_instance);
 
   if (rv)
     return clib_error_return (0, "vnet_create_loopback_interface failed");
@@ -547,15 +636,15 @@ create_simulated_ethernet_interfaces (vlib_main_t * vm,
  *
  * @cliexpar
  * The following two command syntaxes are equivalent:
- * @cliexcmd{loopback create-interface [mac <mac-addr>]}
- * @cliexcmd{create loopback interface [mac <mac-addr>]}
+ * @cliexcmd{loopback create-interface [mac <mac-addr>] [instance <instance>]}
+ * @cliexcmd{create loopback interface [mac <mac-addr>] [instance <instance>]}
  * Example of how to create a loopback interface:
  * @cliexcmd{loopback create-interface}
 ?*/
 /* *INDENT-OFF* */
 VLIB_CLI_COMMAND (create_simulated_ethernet_interface_command, static) = {
   .path = "loopback create-interface",
-  .short_help = "loopback create-interface [mac <mac-addr>]",
+  .short_help = "loopback create-interface [mac <mac-addr>] [instance <instance>]",
   .function = create_simulated_ethernet_interfaces,
 };
 /* *INDENT-ON* */
@@ -566,15 +655,15 @@ VLIB_CLI_COMMAND (create_simulated_ethernet_interface_command, static) = {
  *
  * @cliexpar
  * The following two command syntaxes are equivalent:
- * @cliexcmd{loopback create-interface [mac <mac-addr>]}
- * @cliexcmd{create loopback interface [mac <mac-addr>]}
+ * @cliexcmd{loopback create-interface [mac <mac-addr>] [instance <instance>]}
+ * @cliexcmd{create loopback interface [mac <mac-addr>] [instance <instance>]}
  * Example of how to create a loopback interface:
  * @cliexcmd{create loopback interface}
 ?*/
 /* *INDENT-OFF* */
 VLIB_CLI_COMMAND (create_loopback_interface_command, static) = {
   .path = "create loopback interface",
-  .short_help = "create loopback interface [mac <mac-addr>]",
+  .short_help = "create loopback interface [mac <mac-addr>] [instance <instance>]",
   .function = create_simulated_ethernet_interfaces,
 };
 /* *INDENT-ON* */
@@ -594,12 +683,24 @@ vnet_delete_loopback_interface (u32 sw_if_index)
 {
   vnet_main_t *vnm = vnet_get_main ();
   vnet_sw_interface_t *si;
+  u32 hw_if_index;
+  vnet_hw_interface_t *hw;
+  u32 instance;
 
   if (pool_is_free_index (vnm->interface_main.sw_interfaces, sw_if_index))
     return VNET_API_ERROR_INVALID_SW_IF_INDEX;
 
   si = vnet_get_sw_interface (vnm, sw_if_index);
-  ethernet_delete_interface (vnm, si->hw_if_index);
+  hw_if_index = si->hw_if_index;
+  hw = vnet_get_hw_interface (vnm, hw_if_index);
+  instance = hw->dev_instance;
+
+  if (loopback_instance_free (instance) < 0)
+    {
+      return VNET_API_ERROR_INVALID_SW_IF_INDEX;
+    }
+
+  ethernet_delete_interface (vnm, hw_if_index);
 
   return 0;
 }
index e028fad..f06894e 100644 (file)
@@ -116,6 +116,7 @@ _(PROXY_ARP_INTFC_ENABLE_DISABLE, proxy_arp_intfc_enable_disable)       \
 _(VNET_GET_SUMMARY_STATS, vnet_get_summary_stats)                      \
 _(RESET_FIB, reset_fib)                                                        \
 _(CREATE_LOOPBACK, create_loopback)                                    \
+_(CREATE_LOOPBACK_INSTANCE, create_loopback_instance)                  \
 _(CONTROL_PING, control_ping)                                           \
 _(CLI_REQUEST, cli_request)                                             \
 _(CLI_INBAND, cli_inband)                                              \
@@ -1026,7 +1027,7 @@ vl_api_create_loopback_t_handler (vl_api_create_loopback_t * mp)
   u32 sw_if_index;
   int rv;
 
-  rv = vnet_create_loopback_interface (&sw_if_index, mp->mac_address);
+  rv = vnet_create_loopback_interface (&sw_if_index, mp->mac_address, 0, 0);
 
   /* *INDENT-OFF* */
   REPLY_MACRO2(VL_API_CREATE_LOOPBACK_REPLY,
@@ -1036,6 +1037,26 @@ vl_api_create_loopback_t_handler (vl_api_create_loopback_t * mp)
   /* *INDENT-ON* */
 }
 
+static void vl_api_create_loopback_instance_t_handler
+  (vl_api_create_loopback_instance_t * mp)
+{
+  vl_api_create_loopback_instance_reply_t *rmp;
+  u32 sw_if_index;
+  u8 is_specified = mp->is_specified;
+  u32 user_instance = ntohl (mp->user_instance);
+  int rv;
+
+  rv = vnet_create_loopback_interface (&sw_if_index, mp->mac_address,
+                                      is_specified, user_instance);
+
+  /* *INDENT-OFF* */
+  REPLY_MACRO2(VL_API_CREATE_LOOPBACK_INSTANCE_REPLY,
+  ({
+    rmp->sw_if_index = ntohl (sw_if_index);
+  }));
+  /* *INDENT-ON* */
+}
+
 static void
 vl_api_delete_loopback_t_handler (vl_api_delete_loopback_t * mp)
 {
index c61e31b..ee0c462 100644 (file)
@@ -72,6 +72,18 @@ static void *vl_api_create_loopback_t_print
   FINISH;
 }
 
+static void *vl_api_create_loopback_instance_t_print
+  (vl_api_create_loopback_instance_t * mp, void *handle)
+{
+  u8 *s;
+
+  s = format (0, "SCRIPT: create_loopback ");
+  s = format (s, "mac %U ", format_ethernet_address, &mp->mac_address);
+  s = format (s, "instance %d ", ntohl (mp->user_instance));
+
+  FINISH;
+}
+
 static void *vl_api_delete_loopback_t_print
   (vl_api_delete_loopback_t * mp, void *handle)
 {
@@ -2821,6 +2833,7 @@ foreach_custom_print_no_arg_function
 #undef _
 #define foreach_custom_print_function                                   \
 _(CREATE_LOOPBACK, create_loopback)                                     \
+_(CREATE_LOOPBACK_INSTANCE, create_loopback_instance)                   \
 _(SW_INTERFACE_SET_FLAGS, sw_interface_set_flags)                       \
 _(SW_INTERFACE_ADD_DEL_ADDRESS, sw_interface_add_del_address)           \
 _(SW_INTERFACE_SET_TABLE, sw_interface_set_table)                       \
index ceafc35..8b61f4f 100644 (file)
@@ -534,6 +534,13 @@ static void vl_api_create_loopback_reply_t_handler
           ntohl (mp->retval), ntohl (mp->sw_if_index));
 }
 
+static void vl_api_create_loopback_instance_reply_t_handler
+  (vl_api_create_loopback_instance_reply_t * mp)
+{
+  fformat (stdout, "create loopback status %d, sw_if_index %d\n",
+          ntohl (mp->retval), ntohl (mp->sw_if_index));
+}
+
 static void
 vl_api_sr_tunnel_add_del_reply_t_handler (vl_api_sr_tunnel_add_del_reply_t *
                                          mp)
@@ -598,6 +605,7 @@ _(SW_INTERFACE_IP6ND_RA_PREFIX_REPLY, sw_interface_ip6nd_ra_prefix_reply) \
 _(SW_INTERFACE_IP6_ENABLE_DISABLE_REPLY, sw_interface_ip6_enable_disable_reply) \
 _(SW_INTERFACE_IP6_SET_LINK_LOCAL_ADDRESS_REPLY, sw_interface_ip6_set_link_local_address_reply) \
  _(CREATE_LOOPBACK_REPLY, create_loopback_reply)                       \
+ _(CREATE_LOOPBACK_INSTANCE_REPLY, create_loopback_instance_reply)     \
 _(L2_PATCH_ADD_DEL_REPLY, l2_patch_add_del_reply)                      \
 _(SR_TUNNEL_ADD_DEL_REPLY,sr_tunnel_add_del_reply)          \
 _(SW_INTERFACE_SET_L2_XCONNECT_REPLY, sw_interface_set_l2_xconnect_reply) \
index 7f9c203..a4ba180 100644 (file)
@@ -425,6 +425,34 @@ define create_loopback_reply
   u32 sw_if_index;
 };
 
+/** \brief Create loopback interface instance request
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param mac_address - mac addr to assign to the interface if none-zero
+    @param is_specified - if non-0, a specific user_instance is being requested
+    @param user_instance - requested instance, ~0 => dynamically allocate
+*/
+define create_loopback_instance
+{
+  u32 client_index;
+  u32 context;
+  u8 mac_address[6];
+  u8 is_specified;
+  u32 user_instance;
+};
+
+/** \brief Create loopback interface instance response
+    @param context - sender context, to match reply w/ request
+    @param sw_if_index - sw index of the interface that was created
+    @param retval - return code for the request
+*/
+define create_loopback_instance_reply
+{
+  u32 context;
+  i32 retval;
+  u32 sw_if_index;
+};
+
 /** \brief Delete loopback interface request
     @param client_index - opaque cookie to identify the sender
     @param context - sender context, to match reply w/ request