ipfix-export: support creating multiple exporters 15/34015/3
authorPaul Atkins <patkins@graphiant.com>
Wed, 22 Sep 2021 13:56:17 +0000 (14:56 +0100)
committerNeale Ranns <neale@graphiant.com>
Mon, 22 Nov 2021 09:30:09 +0000 (09:30 +0000)
The existing api set_ipfix_exporter only allows for the creation of
a single exporter. In some cases it is desirable to export data to
multiple different destinations.  Allow users to create multiple
ipfix exporters to support this.

Add a new api that allows for the creation of multiple exporters, and
store them in a pool of exporters. The exporter created by the old API
will always be in index 0 of the pool. Exporters created by the new API
will be given the next available index in the pool, and will return this
index to the API caller so that they can track the exporter they created.
The collector_address is the key for the exporter, so changes can be made
by doing a further call to the API with the same collector_address.

Type: improvement
Signed-off-by: Paul Atkins <patkins@graphiant.com>
Change-Id: Id71c98cffcf8d141d890b40fb90a40b90a91d1d6

src/vnet/ipfix-export/flow_api.c
src/vnet/ipfix-export/flow_report.c
src/vnet/ipfix-export/flow_report.h
src/vnet/ipfix-export/ipfix_export.api

index dc163fe..47e1d11 100644 (file)
 #define REPLY_MSG_ID_BASE frm->msg_id_base
 #include <vlibapi/api_helper_macros.h>
 
+ipfix_exporter_t *
+vnet_ipfix_exporter_lookup (ip4_address_t *ipfix_collector)
+{
+  flow_report_main_t *frm = &flow_report_main;
+  ipfix_exporter_t *exp;
+
+  pool_foreach (exp, frm->exporters)
+    {
+      if (exp->ipfix_collector.as_u32 == ipfix_collector->as_u32)
+       return exp;
+    }
+
+  return NULL;
+}
+
+/*
+ * For backwards compatibility reasons index 0 in the set of exporters
+ * is alwyas used for the exporter created via the set_ipfix_exporter
+ * API.
+ */
+#define USE_INDEX_0   true
+#define USE_ANY_INDEX false
+
 static int
 vl_api_set_ipfix_exporter_t_internal (
   u32 client_index, vl_api_address_t *mp_collector_address,
   u16 mp_collector_port, vl_api_address_t *mp_src_address, u32 mp_vrf_id,
-  u32 mp_path_mtu, u32 mp_template_interval, bool mp_udp_checksum)
+  u32 mp_path_mtu, u32 mp_template_interval, bool mp_udp_checksum,
+  bool use_index_0, bool is_create)
 {
   vlib_main_t *vm = vlib_get_main ();
   flow_report_main_t *frm = &flow_report_main;
-  ipfix_exporter_t *exp = pool_elt_at_index (frm->exporters, 0);
+  ipfix_exporter_t *exp;
   vl_api_registration_t *reg;
   ip4_address_t collector, src;
   u16 collector_port = UDP_DST_PORT_ipfix;
@@ -58,13 +82,48 @@ vl_api_set_ipfix_exporter_t_internal (
   if (!reg)
     return VNET_API_ERROR_UNIMPLEMENTED;
 
+  /* Collector address is the key for the exporter lookup */
+  ip4_address_decode (mp_collector_address->un.ip4, &collector);
+
+  if (use_index_0)
+    /*
+     * In this case we update the existing exporter. There is no delete
+     * for exp[0]
+     */
+    exp = &frm->exporters[0];
+  else
+    {
+      if (is_create)
+       {
+         exp = vnet_ipfix_exporter_lookup (&collector);
+         if (!exp)
+           {
+             /* Create a new exporter instead of updating an existing one */
+             if (pool_elts (frm->exporters) >= IPFIX_EXPORTERS_MAX)
+               return VNET_API_ERROR_INVALID_VALUE;
+             pool_get (frm->exporters, exp);
+             if (!exp)
+               return VNET_API_ERROR_INVALID_VALUE;
+           }
+       }
+      else
+       {
+         /* Delete the exporter */
+         exp = vnet_ipfix_exporter_lookup (&collector);
+         if (!exp)
+           return VNET_API_ERROR_NO_SUCH_ENTRY;
+
+         pool_put (frm->exporters, exp);
+         return 0;
+       }
+    }
+
   if (mp_src_address->af == ADDRESS_IP6 ||
       mp_collector_address->af == ADDRESS_IP6)
     {
       return VNET_API_ERROR_UNIMPLEMENTED;
     }
 
-  ip4_address_decode (mp_collector_address->un.ip4, &collector);
   collector_port = ntohs (mp_collector_port);
   if (collector_port == (u16) ~ 0)
     collector_port = UDP_DST_PORT_ipfix;
@@ -129,11 +188,25 @@ vl_api_set_ipfix_exporter_t_handler (vl_api_set_ipfix_exporter_t *mp)
   int rv = vl_api_set_ipfix_exporter_t_internal (
     mp->client_index, &mp->collector_address, mp->collector_port,
     &mp->src_address, mp->vrf_id, mp->path_mtu, mp->template_interval,
-    mp->udp_checksum);
+    mp->udp_checksum, USE_INDEX_0, 0);
 
   REPLY_MACRO (VL_API_SET_IPFIX_EXPORTER_REPLY);
 }
 
+static void
+vl_api_ipfix_exporter_create_delete_t_handler (
+  vl_api_ipfix_exporter_create_delete_t *mp)
+{
+  vl_api_ipfix_exporter_create_delete_reply_t *rmp;
+  flow_report_main_t *frm = &flow_report_main;
+  int rv = vl_api_set_ipfix_exporter_t_internal (
+    mp->client_index, &mp->collector_address, mp->collector_port,
+    &mp->src_address, mp->vrf_id, mp->path_mtu, mp->template_interval,
+    mp->udp_checksum, USE_ANY_INDEX, mp->is_create);
+
+  REPLY_MACRO (VL_API_IPFIX_EXPORTER_CREATE_DELETE_REPLY);
+}
+
 static void
 vl_api_ipfix_exporter_dump_t_handler (vl_api_ipfix_exporter_dump_t * mp)
 {
index cc04279..38c2454 100644 (file)
@@ -71,8 +71,8 @@ find_stream (ipfix_exporter_t *exp, u32 domain_id, u16 src_port)
 }
 
 int
-send_template_packet (flow_report_main_t * frm,
-                     flow_report_t * fr, u32 * buffer_indexp)
+send_template_packet (flow_report_main_t *frm, ipfix_exporter_t *exp,
+                     flow_report_t *fr, u32 *buffer_indexp)
 {
   u32 bi0;
   vlib_buffer_t *b0;
@@ -82,7 +82,6 @@ send_template_packet (flow_report_main_t * frm,
   udp_header_t *udp;
   vlib_main_t *vm = frm->vlib_main;
   flow_report_stream_t *stream;
-  ipfix_exporter_t *exp = pool_elt_at_index (frm->exporters, 0);
 
   ASSERT (buffer_indexp);
 
@@ -275,49 +274,55 @@ flow_report_process (vlib_main_t * vm,
       vlib_process_wait_for_event_or_clock (vm, wait_time);
       event_type = vlib_process_get_events (vm, &event_data);
       vec_reset_length (event_data);
-      ipfix_exporter_t *exp = pool_elt_at_index (frm->exporters, 0);
-
-      /* 5s delay by default, possibly reduced by template intervals */
-      wait_time = def_wait_time;
-
-      vec_foreach (fr, exp->reports)
+      ipfix_exporter_t *exp;
+      pool_foreach (exp, frm->exporters)
        {
-         f64 next_template;
-         now = vlib_time_now (vm);
-
-         /* Need to send a template packet? */
-         send_template =
-           now > (fr->last_template_sent + exp->template_interval);
-         send_template += fr->last_template_sent == 0;
-         template_bi = ~0;
-         rv = 0;
 
-         if (send_template)
-           rv = send_template_packet (frm, fr, &template_bi);
+         /* 5s delay by default, possibly reduced by template intervals */
+         wait_time = def_wait_time;
 
-         if (rv < 0)
-           continue;
-
-         /* decide if template should be sent sooner than current wait time */
-         next_template =
-           (fr->last_template_sent + exp->template_interval) - now;
-         wait_time = clib_min (wait_time, next_template);
-
-         nf = vlib_get_frame_to_node (vm, ip4_lookup_node_index);
-         nf->n_vectors = 0;
-         to_next = vlib_frame_vector_args (nf);
-
-         if (template_bi != ~0)
+         vec_foreach (fr, exp->reports)
            {
-             to_next[0] = template_bi;
-             to_next++;
-             nf->n_vectors++;
+             f64 next_template;
+             now = vlib_time_now (vm);
+
+             /* Need to send a template packet? */
+             send_template =
+               now > (fr->last_template_sent + exp->template_interval);
+             send_template += fr->last_template_sent == 0;
+             template_bi = ~0;
+             rv = 0;
+
+             if (send_template)
+               rv = send_template_packet (frm, exp, fr, &template_bi);
+
+             if (rv < 0)
+               continue;
+
+             /*
+              * decide if template should be sent sooner than current wait
+              * time
+              */
+             next_template =
+               (fr->last_template_sent + exp->template_interval) - now;
+             wait_time = clib_min (wait_time, next_template);
+
+             nf = vlib_get_frame_to_node (vm, ip4_lookup_node_index);
+             nf->n_vectors = 0;
+             to_next = vlib_frame_vector_args (nf);
+
+             if (template_bi != ~0)
+               {
+                 to_next[0] = template_bi;
+                 to_next++;
+                 nf->n_vectors++;
+               }
+
+             nf = fr->flow_data_callback (frm, exp, fr, nf, to_next,
+                                          ip4_lookup_node_index);
+             if (nf)
+               vlib_put_frame_to_node (vm, ip4_lookup_node_index, nf);
            }
-
-         nf = fr->flow_data_callback (frm, exp, fr, nf, to_next,
-                                      ip4_lookup_node_index);
-         if (nf)
-           vlib_put_frame_to_node (vm, ip4_lookup_node_index, nf);
        }
     }
 
index 7f0c92d..65ddebc 100644 (file)
@@ -20,6 +20,7 @@
 #include <vnet/ethernet/ethernet.h>
 #include <vnet/ethernet/packet.h>
 #include <vnet/ip/ip_packet.h>
+#include <vnet/ip/ip_types.h>
 #include <vnet/ip/ip4_packet.h>
 #include <vnet/ip/ip6_packet.h>
 #include <vnet/udp/udp_packet.h>
@@ -185,6 +186,11 @@ void vnet_stream_reset (ipfix_exporter_t *exp, u32 stream_index);
 int vnet_stream_change (ipfix_exporter_t *exp, u32 old_domain_id,
                        u16 old_src_port, u32 new_domain_id, u16 new_src_port);
 
+/*
+ * Search all the exporters for one that has a matching destination address.
+ */
+ipfix_exporter_t *vnet_ipfix_exporter_lookup (ip4_address_t *ipfix_collector);
+
 #endif /* __included_vnet_flow_report_h__ */
 
 /*
index a70b72b..2b1da36 100644 (file)
@@ -73,6 +73,46 @@ define ipfix_exporter_details
   bool udp_checksum;
 };
 
+
+/** Configure IPFIX exporter within the exporting process.
+    The exporting process can contain multiple independent exporters,
+    each of which have their own state.  The collector_address is the key
+    field that identifies a unique exporter. The already existing API
+    'set_ipfix_exporter' is used to modify a single exporter (which will
+    always have stat index 0).  If more than one exporter is required then
+    they can be created and deleted using this API.
+
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param is_create - True for create, False for delete
+    @param collector_address - address of IPFIX collector
+    @param collector_port - port of IPFIX collector
+    @param src_address - address of IPFIX exporter
+    @param vrf_id - VRF / fib table ID
+    @param path_mtu - Path MTU between exporter and collector
+    @param template_interval - number of seconds after which to resend template
+    @param udp_checksum - UDP checksum calculation enable flag
+*/
+
+define ipfix_exporter_create_delete {
+  u32 client_index;
+  u32 context;
+  bool is_create;
+  vl_api_address_t collector_address;
+  u16 collector_port;
+  vl_api_address_t src_address;
+  u32 vrf_id;
+  u32 path_mtu;
+  u32 template_interval;
+  bool udp_checksum;
+};
+
+define ipfix_exporter_create_delete_reply {
+  u32 context;
+  i32 retval;
+  u32 stat_index;
+};
+
 /** \brief IPFIX classify stream configure request
     @param client_index - opaque cookie to identify the sender
     @param context - sender context, to match reply w/ request