VPP-204 Rework and finish IPFIX implementation 61/2261/7
authorJuraj Sloboda <jsloboda@cisco.com>
Mon, 8 Aug 2016 06:43:42 +0000 (23:43 -0700)
committerDamjan Marion <dmarion.lists@gmail.com>
Wed, 7 Sep 2016 07:35:50 +0000 (07:35 +0000)
Rework flow report registration system - add streams
Add support for IPv6 and src and dst ports for TCP and UDP protocols
Implement binary API for IPFIX classifier module

Change-Id: Id05cc0127a7b95ceaeebf9c79a32c6936449bd63
Signed-off-by: Juraj Sloboda <jsloboda@cisco.com>
vnet/vnet/flow/flow_report.c
vnet/vnet/flow/flow_report.h
vnet/vnet/flow/flow_report_classify.c
vnet/vnet/flow/flow_report_classify.h
vpp-api-test/vat/api_format.c
vpp/vpp-api/api.c
vpp/vpp-api/custom_dump.c
vpp/vpp-api/vpe.api

index 38ad613..932613d 100644 (file)
 
 flow_report_main_t flow_report_main;
 
+static_always_inline u8 stream_index_valid (u32 index)
+{
+  flow_report_main_t * frm = &flow_report_main;
+  return index < vec_len(frm->streams) &&
+         frm->streams[index].domain_id != ~0;
+}
+
+static_always_inline flow_report_stream_t * add_stream (void)
+{
+  flow_report_main_t * frm = &flow_report_main;
+  u32 i;
+  for (i = 0; i < vec_len(frm->streams); i++)
+    if (!stream_index_valid(i))
+      return &frm->streams[i];
+  u32 index = vec_len(frm->streams);
+  vec_validate(frm->streams, index);
+  return &frm->streams[index];
+}
+
+static_always_inline void delete_stream (u32 index)
+{
+  flow_report_main_t * frm = &flow_report_main;
+  ASSERT (index < vec_len(frm->streams));
+  ASSERT (frm->streams[index].domain_id != ~0);
+  frm->streams[index].domain_id = ~0;
+}
+
+static i32 find_stream (u32 domain_id, u16 src_port)
+{
+  flow_report_main_t * frm = &flow_report_main;
+  flow_report_stream_t * stream;
+  u32 i;
+  for (i = 0; i < vec_len(frm->streams); i++)
+    if (stream_index_valid(i)) {
+      stream = &frm->streams[i];
+      if (domain_id == stream->domain_id) {
+        if (src_port != stream->src_port)
+          return -2;
+        return i;
+      } else if (src_port == stream->src_port) {
+        return -2;
+      }
+    }
+  return -1;
+}
+
 int send_template_packet (flow_report_main_t *frm, 
                           flow_report_t *fr,
                           u32 * buffer_indexp)
@@ -31,6 +77,7 @@ int send_template_packet (flow_report_main_t *frm,
   ip4_header_t * ip;
   udp_header_t * udp;
   vlib_main_t * vm = frm->vlib_main;
+  flow_report_stream_t * stream;
 
   ASSERT (buffer_indexp);
 
@@ -82,12 +129,22 @@ int send_template_packet (flow_report_main_t *frm,
      (vlib_time_now(frm->vlib_main) - frm->vlib_time_0));
   h->export_time = clib_host_to_net_u32(h->export_time);
 
+  stream = &frm->streams[fr->stream_index];
+
   /* FIXUP: message header sequence_number. Templates do not increase it */
-  h->sequence_number = clib_host_to_net_u32(fr->sequence_number);
+  h->sequence_number = clib_host_to_net_u32(stream->sequence_number);
 
   /* FIXUP: udp length */
   udp->length = clib_host_to_net_u16 (b0->current_length - sizeof (*ip));
 
+  if (frm->udp_checksum)
+    {
+      /* RFC 7011 section 10.3.2. */
+      udp->checksum = ip4_tcp_udp_compute_checksum (vm, b0, ip);
+      if (udp->checksum == 0)
+        udp->checksum = 0xffff;
+    }
+
   *buffer_indexp = bi0;
 
   fr->last_template_sent = vlib_time_now (vm);
@@ -178,11 +235,19 @@ int vnet_flow_report_add_del (flow_report_main_t *frm,
   int i;
   int found_index = ~0;
   flow_report_t *fr;
+  flow_report_stream_t * stream;
+  u32 si;
   
+  si = find_stream(a->domain_id, a->src_port);
+  if (si == -2)
+    return VNET_API_ERROR_INVALID_VALUE;
+  if (si == -1 && a->is_add == 0)
+    return VNET_API_ERROR_NO_SUCH_ENTRY;
+
   for (i = 0; i < vec_len(frm->reports); i++)
     {
       fr = vec_elt_at_index (frm->reports, i);
-      if (fr->opaque == a->opaque
+      if (fr->opaque.as_uword == a->opaque.as_uword
           && fr->rewrite_callback == a->rewrite_callback
           && fr->flow_data_callback == a->flow_data_callback)
         {
@@ -196,16 +261,37 @@ int vnet_flow_report_add_del (flow_report_main_t *frm,
       if (found_index != ~0)
         {
           vec_delete (frm->reports, 1, found_index);
+          stream = &frm->streams[si];
+          stream->n_reports--;
+          if (stream->n_reports == 0)
+            delete_stream(si);
           return 0;
         }
       return VNET_API_ERROR_NO_SUCH_ENTRY;
     }
 
+  if (found_index != ~0)
+    return VNET_API_ERROR_VALUE_EXIST;
+
+  if (si == -1)
+    {
+      stream = add_stream();
+      stream->domain_id = a->domain_id;
+      stream->src_port = a->src_port;
+      stream->sequence_number = 0;
+      stream->n_reports = 0;
+      si = stream - frm->streams;
+    }
+  else
+    stream = &frm->streams[si];
+
+  stream->n_reports++;
+
   vec_add2 (frm->reports, fr, 1);
 
-  fr->sequence_number = 0;
-  fr->domain_id = a->domain_id;
-  fr->src_port = a->src_port;
+  fr->stream_index = si;
+  fr->template_id = 256 + stream->next_template_no;
+  stream->next_template_no = (stream->next_template_no + 1) % (65536 - 256);
   fr->update_rewrite = 1;
   fr->opaque = a->opaque;
   fr->rewrite_callback = a->rewrite_callback;
@@ -217,18 +303,51 @@ int vnet_flow_report_add_del (flow_report_main_t *frm,
 void vnet_flow_reports_reset (flow_report_main_t * frm)
 {
   flow_report_t *fr;
+  u32 i;
+
+  for (i = 0; i < vec_len(frm->streams); i++)
+    if (stream_index_valid(i))
+      frm->streams[i].sequence_number = 0;
+
   vec_foreach (fr, frm->reports)
     {
-         fr->sequence_number = 0;
       fr->update_rewrite = 1;
       fr->last_template_sent = 0;
     }
 }
 
+void vnet_stream_reset (flow_report_main_t * frm, u32 stream_index)
+{
+  flow_report_t *fr;
+
+  frm->streams[stream_index].sequence_number = 0;
+
+  vec_foreach (fr, frm->reports)
+    if (frm->reports->stream_index == stream_index) {
+      fr->update_rewrite = 1;
+      fr->last_template_sent = 0;
+    }
+}
+
+int vnet_stream_change (flow_report_main_t * frm,
+                        u32 old_domain_id, u16 old_src_port,
+                        u32 new_domain_id, u16 new_src_port)
+{
+  i32 stream_index = find_stream (old_domain_id, old_src_port);
+  if (stream_index < 0)
+    return 1;
+  flow_report_stream_t * stream = &frm->streams[stream_index];
+  stream->domain_id = new_domain_id;
+  stream->src_port = new_src_port;
+  if (old_domain_id != new_domain_id || old_src_port != new_src_port)
+    vnet_stream_reset (frm, stream_index);
+  return 0;
+}
+
 static clib_error_t *
-set_ipfix_command_fn (vlib_main_t * vm,
-                unformat_input_t * input,
-                vlib_cli_command_t * cmd)
+set_ipfix_exporter_command_fn (vlib_main_t * vm,
+                               unformat_input_t * input,
+                               vlib_cli_command_t * cmd)
 {
   flow_report_main_t * frm = &flow_report_main;
   ip4_address_t collector, src;
@@ -240,6 +359,7 @@ set_ipfix_command_fn (vlib_main_t * vm,
   src.as_u32 = 0;
   u32 path_mtu = 512; // RFC 7011 section 10.3.3.
   u32 template_interval = 20;
+  u8 udp_checksum = 0;
 
   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
     if (unformat (input, "collector %U", unformat_ip4_address, &collector))
@@ -261,6 +381,8 @@ set_ipfix_command_fn (vlib_main_t * vm,
       ;
     else if (unformat (input, "template-interval %u", &template_interval))
       ;
+    else if (unformat (input, "udp-checksum"))
+      udp_checksum = 1;
     else
       break;
   }
@@ -289,13 +411,16 @@ set_ipfix_command_fn (vlib_main_t * vm,
   frm->fib_index = fib_index;
   frm->path_mtu = path_mtu;
   frm->template_interval = template_interval;
+  frm->udp_checksum = udp_checksum;
   
   vlib_cli_output (vm, "Collector %U, src address %U, "
                           "fib index %d, path MTU %u, "
-                          "template resend interval %us",
+                          "template resend interval %us, "
+                          "udp checksum %s",
                    format_ip4_address, &frm->ipfix_collector,
                    format_ip4_address, &frm->src_address,
-                   fib_index, path_mtu, template_interval);
+                   fib_index, path_mtu, template_interval,
+                   udp_checksum ? "enabled" : "disabled");
 
   /* Turn on the flow reporting process */
   vlib_process_signal_event (vm, flow_report_process_node.index,
@@ -303,14 +428,15 @@ set_ipfix_command_fn (vlib_main_t * vm,
   return 0;
 }
 
-VLIB_CLI_COMMAND (set_ipfix_command, static) = {
-    .path = "set ipfix",
-    .short_help = "set ipfix collector <ip4-address> "
-                  "[port <port>] "
+VLIB_CLI_COMMAND (set_ipfix_exporter_command, static) = {
+    .path = "set ipfix exporter",
+    .short_help = "set ipfix exporter "
+                  "collector <ip4-address> [port <port>] "
                   "src <ip4-address> [fib-id <fib-id>] "
                   "[path-mtu <path-mtu>] "
                   "[template-interval <template-interval>]",
-    .function = set_ipfix_command_fn,
+                  "[udp-checksum]",
+    .function = set_ipfix_exporter_command_fn,
 };
 
 static clib_error_t * 
@@ -322,6 +448,7 @@ flow_report_init (vlib_main_t *vm)
   frm->vnet_main = vnet_get_main();
   frm->unix_time_0 = time(0);
   frm->vlib_time_0 = vlib_time_now(frm->vlib_main);
+  frm->fib_index = ~0;
 
   return 0;
 }
index 27aa81a..7b9fc7b 100644 (file)
@@ -51,12 +51,25 @@ typedef vlib_frame_t * (vnet_flow_data_callback_t) (struct flow_report_main *,
                                                     struct flow_report *,
                                                     vlib_frame_t *, u32 *, 
                                                     u32);
+
+typedef union {
+  void * as_ptr;
+  uword as_uword;
+} opaque_t;
+
+typedef struct {
+  u32 domain_id;
+  u32 sequence_number;
+  u16 src_port;
+  u16 n_reports;
+  u16 next_template_no;
+} flow_report_stream_t;
+
 typedef struct flow_report {
   /* ipfix rewrite, set by callback */
   u8 * rewrite;
-  u32 sequence_number;
-  u32 domain_id;
-  u16 src_port;
+  u16 template_id;
+  u32 stream_index;
   f64 last_template_sent;
   int update_rewrite;
 
@@ -64,7 +77,7 @@ typedef struct flow_report {
   uword * fields_to_send;
 
   /* Opaque data */
-  void * opaque;
+  opaque_t opaque;
 
   /* build-the-rewrite callback */
   vnet_flow_rewrite_callback_t *rewrite_callback;
@@ -75,6 +88,7 @@ typedef struct flow_report {
 
 typedef struct flow_report_main {
   flow_report_t * reports;
+  flow_report_stream_t * streams;
 
   /* ipfix collector ip address, port, our ip address, fib index */
   ip4_address_t ipfix_collector;
@@ -88,6 +102,9 @@ typedef struct flow_report_main {
   /* time interval in seconds after which to resend templates */
   u32 template_interval;
 
+  /* UDP checksum calculation enable flag */
+  u8 udp_checksum;
+
   /* time scale transform. Joy. */
   u32 unix_time_0;
   f64 vlib_time_0;
@@ -106,7 +123,7 @@ int vnet_flow_report_enable_disable (u32 sw_if_index, u32 table_index,
 typedef struct {
   vnet_flow_data_callback_t *flow_data_callback;
   vnet_flow_rewrite_callback_t *rewrite_callback;
-  void * opaque;
+  opaque_t opaque;
   int is_add;
   u32 domain_id;
   u16 src_port;
@@ -117,4 +134,10 @@ int vnet_flow_report_add_del (flow_report_main_t *frm,
 
 void vnet_flow_reports_reset (flow_report_main_t * frm);
 
+void vnet_stream_reset (flow_report_main_t * frm, u32 stream_index);
+
+int vnet_stream_change (flow_report_main_t * frm,
+                        u32 old_domain_id, u16 old_src_port,
+                        u32 new_domain_id, u16 new_src_port);
+
 #endif /* __included_vnet_flow_report_h__ */
index f13f4fe..9540379 100644 (file)
 #include <vnet/flow/flow_report_classify.h>
 #include <vnet/api_errno.h>
 
+/* Common prefix of tcp and udp headers
+ * containing only source and destination port fields */
 typedef struct {
-  u32 classify_table_index;
-} flow_report_classify_main_t;
+  u16 src_port, dst_port;
+} tcpudp_header_t;
 
 flow_report_classify_main_t flow_report_classify_main;
 
-static u8 * template_rewrite (flow_report_main_t * frm,
-                              flow_report_t * fr,
-                              ip4_address_t * collector_address,
-                              ip4_address_t * src_address,
-                              u16 collector_port)
+u8 * ipfix_classify_template_rewrite (flow_report_main_t * frm,
+                                      flow_report_t * fr,
+                                      ip4_address_t * collector_address,
+                                      ip4_address_t * src_address,
+                                      u16 collector_port)
 {
+  flow_report_classify_main_t * fcm = &flow_report_classify_main;
   vnet_classify_table_t * tblp;
   vnet_classify_main_t * vcm = &vnet_classify_main;
-  flow_report_classify_main_t *fcm =
-    (flow_report_classify_main_t *) fr->opaque;
+  u32 flow_table_index = fr->opaque.as_uword;
+  u8 * ip_start;
   ip4_header_t * ip;
+  ip6_header_t * ip6;
+  tcpudp_header_t * tcpudp;
   udp_header_t * udp;
   ipfix_message_header_t * h;
   ipfix_set_header_t * s;
@@ -44,20 +49,28 @@ static u8 * template_rewrite (flow_report_main_t * frm,
   i32 l3_offset = -2;  /* sizeof (ethernet_header_t) - sizeof (u32x4) */
   u32 field_count = 0;
   u32 field_index = 0;
-  
-  tblp = pool_elt_at_index (vcm->tables, fcm->classify_table_index);
+  flow_report_stream_t * stream;
+  u8 ip_version;
+  u8 transport_protocol;
+
+  stream = &frm->streams[fr->stream_index];
+
+  ipfix_classify_table_t * table = &fcm->tables[flow_table_index];
+
+  ip_version = table->ip_version;
+  transport_protocol = table->transport_protocol;
+
+  tblp = pool_elt_at_index (vcm->tables, table->classify_table_index);
 
   /* 
    * Mumble, assumes that we're not classifying on L2 or first 2 octets
    * of L3..
    */
 
-  ip = (ip4_header_t *)(((u8 *)(tblp->mask)) + l3_offset);
-  udp = (udp_header_t *)(ip+1);
-
   /* Determine field count */
+  ip_start = ((u8 *)(tblp->mask)) + l3_offset;
 #define _(field,mask,item,length)                                       \
-  if ((field) == (mask))                                                \
+  if (memcmp(&field, &mask, length) == 0)                               \
     {                                                                   \
       field_count++;                                                    \
                                                                         \
@@ -65,9 +78,9 @@ static u8 * template_rewrite (flow_report_main_t * frm,
                                             field_index, 1);            \
     }                                                                   \
   field_index++;
-  
   foreach_ipfix_field;
 #undef _
+
   /* Add packetTotalCount manually */
   field_count += 1;
 
@@ -92,19 +105,18 @@ static u8 * template_rewrite (flow_report_main_t * frm,
   ip->protocol = IP_PROTOCOL_UDP;
   ip->src_address.as_u32 = src_address->as_u32;
   ip->dst_address.as_u32 = collector_address->as_u32;
-  udp->src_port = clib_host_to_net_u16 (fr->src_port);
+  udp->src_port = clib_host_to_net_u16 (stream->src_port);
   udp->dst_port = clib_host_to_net_u16 (collector_port);
   udp->length = clib_host_to_net_u16 (vec_len(rewrite) - sizeof (*ip));
 
   /* FIXUP: message header export_time */ 
   /* FIXUP: message header sequence_number */
-  h->domain_id = clib_host_to_net_u32 (fr->domain_id);
+  h->domain_id = clib_host_to_net_u32 (stream->domain_id);
 
   /* Take another trip through the mask and build the template */
-  ip = (ip4_header_t *)(((u8 *)(tblp->mask)) + l3_offset);
-  udp = (udp_header_t *)(ip+1);
+  ip_start = ((u8 *)(tblp->mask)) + l3_offset;
 #define _(field,mask,item,length)                               \
-  if ((field) == (mask))                                        \
+  if (memcmp(&field, &mask, length) == 0)                       \
     {                                                           \
       f->e_id_length = ipfix_e_id_length (0 /* enterprise */,   \
                                           item, length);        \
@@ -123,7 +135,7 @@ static u8 * template_rewrite (flow_report_main_t * frm,
   
   ASSERT (f - first_field);
   /* Field count in this template */
-  t->id_count = ipfix_id_count (256 /* template_id */, f - first_field);
+  t->id_count = ipfix_id_count (fr->template_id, f - first_field);
 
   /* set length in octets*/
   s->set_id_length = ipfix_set_id_length (2 /* set_id */, (u8 *) f - (u8 *)s);
@@ -137,16 +149,16 @@ static u8 * template_rewrite (flow_report_main_t * frm,
   return rewrite;
 }
 
-static vlib_frame_t * send_flows (flow_report_main_t * frm, 
-                                  flow_report_t * fr,
-                                  vlib_frame_t * f, u32 * to_next, 
-                                  u32 node_index)
+vlib_frame_t * ipfix_classify_send_flows (flow_report_main_t * frm,
+                                          flow_report_t * fr,
+                                          vlib_frame_t * f,
+                                          u32 * to_next,
+                                          u32 node_index)
 {
+  flow_report_classify_main_t * fcm = &flow_report_classify_main;
   vnet_classify_main_t * vcm = &vnet_classify_main;
-  flow_report_classify_main_t * fcm =
-    (flow_report_classify_main_t *) fr->opaque;
-  vnet_classify_table_t * t = 
-    pool_elt_at_index (vcm->tables, fcm->classify_table_index);
+  u32 flow_table_index = fr->opaque.as_uword;
+  vnet_classify_table_t * t;
   vnet_classify_bucket_t * b;
   vnet_classify_entry_t * v, * save_v;
   vlib_buffer_t *b0 = 0;
@@ -157,14 +169,28 @@ static vlib_frame_t * send_flows (flow_report_main_t * frm,
   ip4_ipfix_template_packet_t * tp;
   ipfix_message_header_t * h = 0;
   ipfix_set_header_t * s = 0;
+  u8 * ip_start;
   ip4_header_t * ip;
+  ip6_header_t * ip6;
+  tcpudp_header_t * tcpudp;
   udp_header_t * udp;
   int field_index;
-  ip4_header_t * match;
   u32 records_this_buffer;
   u16 new_l0, old_l0;
   ip_csum_t sum0;
   vlib_main_t * vm = frm->vlib_main;
+  flow_report_stream_t * stream;
+  u8 ip_version;
+  u8 transport_protocol;
+
+  stream = &frm->streams[fr->stream_index];
+
+  ipfix_classify_table_t * table = &fcm->tables[flow_table_index];
+
+  ip_version = table->ip_version;
+  transport_protocol = table->transport_protocol;
+
+  t = pool_elt_at_index (vcm->tables, table->classify_table_index);
   
   while (__sync_lock_test_and_set (t->writer_lock, 1))
     ; 
@@ -216,19 +242,16 @@ static vlib_frame_t * send_flows (flow_report_main_t * frm,
                   h->export_time = clib_host_to_net_u32(h->export_time);
                   
                   /* FIXUP: message header sequence_number */
-                  h->sequence_number = fr->sequence_number;
+                  h->sequence_number = stream->sequence_number;
                   h->sequence_number = clib_host_to_net_u32 (h->sequence_number);
 
                   next_offset = (u32) (((u8 *)(s+1)) - (u8 *)tp);
                   record_offset = next_offset;
                   records_this_buffer = 0;
                 }
-              
+
               field_index = 0;
-              match = (ip4_header_t *) (((u8 *)v->key) - 2);
-              ip = match;
-              udp = (udp_header_t * )(ip+1);
-              
+              ip_start = ((u8 *)v->key) - 2;
 #define _(field,mask,item,length)                                       \
               if (clib_bitmap_get (fr->fields_to_send, field_index))    \
                 {                                                       \
@@ -239,7 +262,7 @@ static vlib_frame_t * send_flows (flow_report_main_t * frm,
               field_index++;
               foreach_ipfix_field;
 #undef _
-              
+
               /* Add packetTotalCount manually */
               {
                 u64 packets = clib_host_to_net_u64 (v->hits);
@@ -247,7 +270,7 @@ static vlib_frame_t * send_flows (flow_report_main_t * frm,
                 next_offset += sizeof (packets);
               }
               records_this_buffer++;
-              fr->sequence_number++;
+              stream->sequence_number++;
               
               /* Next record will have the same size as this record */
               u32 next_record_size = next_offset - record_offset;
@@ -255,7 +278,7 @@ static vlib_frame_t * send_flows (flow_report_main_t * frm,
 
               if (next_offset + next_record_size > frm->path_mtu)
                 {
-                  s->set_id_length = ipfix_set_id_length (256 /* template ID*/, 
+                  s->set_id_length = ipfix_set_id_length (fr->template_id,
                                                           next_offset - 
                                                           (sizeof (*ip) + sizeof (*udp) +
                                                            sizeof (*h)));
@@ -280,7 +303,15 @@ static vlib_frame_t * send_flows (flow_report_main_t * frm,
                   ip->length = new_l0;
                   udp->length = 
                       clib_host_to_net_u16 (b0->current_length - sizeof (*ip));
-                  
+
+                  if (frm->udp_checksum)
+                    {
+                      /* RFC 7011 section 10.3.2. */
+                      udp->checksum = ip4_tcp_udp_compute_checksum (vm, b0, ip);
+                      if (udp->checksum == 0)
+                        udp->checksum = 0xffff;
+                    }
+
                   ASSERT (ip->checksum == ip4_header_checksum (ip));
 
                   to_next[0] = bi0;
@@ -304,7 +335,7 @@ static vlib_frame_t * send_flows (flow_report_main_t * frm,
  flush:
   if (b0)
     {
-        s->set_id_length = ipfix_set_id_length (256 /* template ID*/, 
+        s->set_id_length = ipfix_set_id_length (fr->template_id,
                                                 next_offset - 
                                                 (sizeof (*ip) + sizeof (*udp) +
                                                  sizeof (*h)));
@@ -328,6 +359,14 @@ static vlib_frame_t * send_flows (flow_report_main_t * frm,
       ip->length = new_l0;
       udp->length = clib_host_to_net_u16 (b0->current_length - sizeof (*ip));
 
+      if (frm->udp_checksum)
+        {
+          /* RFC 7011 section 10.3.2. */
+          udp->checksum = ip4_tcp_udp_compute_checksum (vm, b0, ip);
+          if (udp->checksum == 0)
+          udp->checksum = 0xffff;
+        }
+
       ASSERT (ip->checksum == ip4_header_checksum (ip));
       
       to_next[0] = bi0;
@@ -341,47 +380,81 @@ static vlib_frame_t * send_flows (flow_report_main_t * frm,
   return f;
 }
 
-
 static clib_error_t *
-flow_classify_command_fn (vlib_main_t * vm,
-                unformat_input_t * input,
-                vlib_cli_command_t * cmd)
+ipfix_classify_table_add_del_command_fn (vlib_main_t * vm,
+                                         unformat_input_t * input,
+                                         vlib_cli_command_t * cmd)
 {
   flow_report_classify_main_t *fcm = &flow_report_classify_main;
   flow_report_main_t *frm = &flow_report_main;
   vnet_flow_report_add_del_args_t args;
+  ipfix_classify_table_t * table;
   int rv;
-  int is_add = 1;
-  u32 domain_id = 0;
-  u32 src_port = UDP_DST_PORT_ipfix;
+  int is_add = -1;
+  u32 classify_table_index;
+  u8 ip_version = 0;
+  u8 transport_protocol = 255;
+
+  if (fcm->src_port == 0)
+    clib_error_return (0, "call 'set ipfix classify stream' first");
 
-  domain_id = 0;
-  fcm->classify_table_index = ~0;
   memset (&args, 0, sizeof (args));
 
   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
-    if (unformat (input, "table %d", &fcm->classify_table_index))
-      ;
-    else if (unformat (input, "domain %d", &domain_id))
-      ;
-    else if (unformat (input, "src-port %d", &src_port))
-      ;
+    if (unformat (input, "add"))
+      is_add = 1;
     else if (unformat (input, "del"))
       is_add = 0;
+    else if (unformat (input, "%d", &classify_table_index))
+      ;
+    else if (unformat (input, "ip4"))
+      ip_version = 4;
+    else if (unformat (input, "ip6"))
+      ip_version = 6;
+    else if (unformat (input, "tcp"))
+      transport_protocol = 6;
+    else if (unformat (input, "udp"))
+      transport_protocol = 17;
     else
       return clib_error_return (0, "unknown input `%U'",
                                 format_unformat_error, input);
   }
 
-  if (fcm->classify_table_index == ~0)
+  if (is_add == -1)
+    return clib_error_return (0, "expecting: add|del");
+  if (classify_table_index == ~0)
     return clib_error_return (0, "classifier table not specified");
+  if (ip_version == 0)
+    return clib_error_return (0, "IP version not specified");
+
+  table = 0;
+  int i;
+  for (i = 0; i < vec_len(fcm->tables); i++)
+    if (ipfix_classify_table_index_valid(i))
+      if (fcm->tables[i].classify_table_index == classify_table_index) {
+        table = &fcm->tables[i];
+        break;
+      }
+
+  if (is_add) {
+    if (table)
+      return clib_error_return (0, "Specified classifier table already used");
+    table = ipfix_classify_add_table();
+    table->classify_table_index = classify_table_index;
+  } else {
+    if (!table)
+      return clib_error_return (0, "Specified classifier table not registered");
+  }
+
+  table->ip_version = ip_version;
+  table->transport_protocol = transport_protocol;
 
-  args.opaque = (void *) fcm;
-  args.rewrite_callback = template_rewrite;
-  args.flow_data_callback = send_flows;
+  args.opaque.as_uword = table - fcm->tables;
+  args.rewrite_callback = ipfix_classify_template_rewrite;
+  args.flow_data_callback = ipfix_classify_send_flows;
   args.is_add = is_add;
-  args.domain_id = domain_id;
-  args.src_port = (u16)src_port;
+  args.domain_id = fcm->domain_id;
+  args.src_port = fcm->src_port;
 
   rv = vnet_flow_report_add_del (frm, &args);
 
@@ -390,18 +463,73 @@ flow_classify_command_fn (vlib_main_t * vm,
     case 0:
       break;
     case VNET_API_ERROR_NO_SUCH_ENTRY:
-      return clib_error_return (0, "registration not found...");
+      return clib_error_return (0, "Flow report not found");
+    case VNET_API_ERROR_VALUE_EXIST:
+      return clib_error_return (0, "Flow report already exists");
+    case VNET_API_ERROR_INVALID_VALUE:
+      return clib_error_return (0, "Expecting either still unused values "
+                                   "for both domain_id and src_port "
+                                   "or already used values for both fields");
     default:
       return clib_error_return (0, "vnet_flow_report_add_del returned %d", rv);
     }
 
+  if (is_add) {
+    if (rv != 0)
+      ipfix_classify_delete_table(table - fcm->tables);
+  } else {
+    if (rv == 0)
+      ipfix_classify_delete_table(table - fcm->tables);
+  }
+
+  return 0;
+}
+
+VLIB_CLI_COMMAND (ipfix_classify_table_add_del_command, static) = {
+  .path = "ipfix classify table",
+  .short_help = "ipfix classify table add|del <table-index>",
+  .function = ipfix_classify_table_add_del_command_fn,
+};
+
+static clib_error_t *
+set_ipfix_classify_stream_command_fn (vlib_main_t * vm,
+                                      unformat_input_t * input,
+                                      vlib_cli_command_t * cmd)
+{
+  flow_report_classify_main_t *fcm = &flow_report_classify_main;
+  flow_report_main_t *frm = &flow_report_main;
+  u32 domain_id = 1;
+  u32 src_port = UDP_DST_PORT_ipfix;
+
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
+    if (unformat (input, "domain %d", &domain_id))
+      ;
+    else if (unformat (input, "src-port %d", &src_port))
+      ;
+    else
+      return clib_error_return (0, "unknown input `%U'",
+                                format_unformat_error, input);
+  }
+
+  if (fcm->src_port != 0 &&
+      (fcm->domain_id != domain_id ||
+       fcm->src_port != (u16)src_port)) {
+    int rv = vnet_stream_change (frm, fcm->domain_id, fcm->src_port,
+                                 domain_id, (u16)src_port);
+    ASSERT (rv == 0);
+  }
+
+  fcm->domain_id = domain_id;
+  fcm->src_port = (u16)src_port;
+
   return 0;
 }
 
-VLIB_CLI_COMMAND (flow_classify_command, static) = {
-  .path = "flow classify",
-  .short_help = "flow classify",
-  .function = flow_classify_command_fn,
+VLIB_CLI_COMMAND (set_ipfix_classify_stream_command, static) = {
+  .path = "set ipfix classify stream",
+  .short_help = "set ipfix classify stream"
+                "[domain <domain-id>] [src-port <src-port>]",
+  .function = set_ipfix_classify_stream_command_fn,
 };
 
 static clib_error_t *
index c72ef0e..77d98b5 100644 (file)
 #ifndef __included_flow_report_classify_h__
 #define __included_flow_report_classify_h__
 
-/* Note: add +2 to udp (src,dst) port enum values to get TCP values */
-#define foreach_ipfix_field                                             \
-_(ip->src_address.as_u32, 0xffffffff, sourceIPv4Address, 4)             \
-_(ip->dst_address.as_u32, 0xffffffff, destinationIPv4Address, 4)        \
-_(ip->protocol, 0xFF, protocolIdentifier, 1)                            \
-_(udp->src_port, 0xFFFF, udpSourcePort, 2)                              \
-_(udp->dst_port, 0xFFFF, udpDestinationPort, 2)
+#define foreach_ipfix_ip4_field                                             \
+_(ip->src_address.as_u32, ((u32[]){0xFFFFFFFF}), sourceIPv4Address, 4)      \
+_(ip->dst_address.as_u32, ((u32[]){0xFFFFFFFF}), destinationIPv4Address, 4) \
+_(ip->protocol, ((u8[]){0xFF}), protocolIdentifier, 1)
+
+#define foreach_ipfix_ip6_field                                             \
+_(ip6->src_address.as_u8,                                                   \
+  ((u32[]){0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF}),                   \
+  sourceIPv6Address, 16)                                                    \
+_(ip6->dst_address.as_u8,                                                   \
+  ((u32[]){0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF}),                   \
+  destinationIPv6Address, 16)                                               \
+_(ip6->protocol, ((u8[]){0xFF}), protocolIdentifier, 1)
+
+#define foreach_ipfix_tcpudp_field                                          \
+_(tcpudp->src_port, ((u16[]){0xFFFF}), sourceTransportPort, 2)              \
+_(tcpudp->dst_port, ((u16[]){0xFFFF}), destinationTransportPort, 2)
+
+#define foreach_ipfix_tcp_field                                             \
+_(tcpudp->src_port, ((u16[]){0xFFFF}), tcpSourcePort, 2)                    \
+_(tcpudp->dst_port, ((u16[]){0xFFFF}), tcpDestinationPort, 2)
+
+#define foreach_ipfix_udp_field                                             \
+_(tcpudp->src_port, ((u16[]){0xFFFF}), udpSourcePort, 2)                    \
+_(tcpudp->dst_port, ((u16[]){0xFFFF}), udpDestinationPort, 2)
+
+#define foreach_ipfix_transport_protocol_field                              \
+  switch (transport_protocol) {                                             \
+    case 255:                                                               \
+      foreach_ipfix_tcpudp_field;                                           \
+      break;                                                                \
+    case 6:                                                                 \
+      foreach_ipfix_tcp_field;                                              \
+      break;                                                                \
+    case 17:                                                                \
+      foreach_ipfix_udp_field;                                              \
+      break;                                                                \
+  }
+
+#define foreach_ipfix_field                                                 \
+  if (ip_version == 4) {                                                    \
+    ip = (ip4_header_t *)ip_start;                                          \
+    tcpudp = (tcpudp_header_t *)(ip+1);                                     \
+    foreach_ipfix_ip4_field;                                                \
+  } else {                                                                  \
+    ip6 = (ip6_header_t *)ip_start;                                         \
+    tcpudp = (tcpudp_header_t *)(ip6+1);                                    \
+    foreach_ipfix_ip6_field;                                                \
+  }                                                                         \
+  foreach_ipfix_transport_protocol_field
+
+typedef struct {
+  u32 classify_table_index;
+  u8 ip_version;
+  u8 transport_protocol;
+} ipfix_classify_table_t;
+
+typedef struct {
+  u32 domain_id;
+  u16 src_port;
+  ipfix_classify_table_t * tables;
+} flow_report_classify_main_t;
+
+extern flow_report_classify_main_t flow_report_classify_main;
+
+static_always_inline u8 ipfix_classify_table_index_valid (u32 index)
+{
+  flow_report_classify_main_t * fcm = &flow_report_classify_main;
+  return index < vec_len(fcm->tables) &&
+         fcm->tables[index].classify_table_index != ~0;
+}
+
+static_always_inline ipfix_classify_table_t * ipfix_classify_add_table (void)
+{
+  flow_report_classify_main_t * fcm = &flow_report_classify_main;
+  u32 i;
+  for (i = 0; i < vec_len(fcm->tables); i++)
+    if (!ipfix_classify_table_index_valid(i))
+      return &fcm->tables[i];
+  u32 index = vec_len(fcm->tables);
+  vec_validate(fcm->tables, index);
+  return &fcm->tables[index];
+}
+
+static_always_inline void ipfix_classify_delete_table (u32 index)
+{
+  flow_report_classify_main_t * fcm = &flow_report_classify_main;
+  ASSERT (index < vec_len(fcm->tables));
+  ASSERT (fcm->tables[index].classify_table_index != ~0);
+  fcm->tables[index].classify_table_index = ~0;
+}
+
+u8 * ipfix_classify_template_rewrite (flow_report_main_t * frm,
+                                      flow_report_t * fr,
+                                      ip4_address_t * collector_address,
+                                      ip4_address_t * src_address,
+                                      u16 collector_port);
+
+vlib_frame_t * ipfix_classify_send_flows (flow_report_main_t * frm,
+                                          flow_report_t * fr,
+                                          vlib_frame_t * f,
+                                          u32 * to_next,
+                                          u32 node_index);
 
 #endif /* __included_flow_report_classify_h__ */
index 3b93c98..3c65d78 100644 (file)
@@ -3426,7 +3426,9 @@ _(af_packet_delete_reply)                               \
 _(policer_classify_set_interface_reply)                 \
 _(netmap_create_reply)                                  \
 _(netmap_delete_reply)                                  \
-_(ipfix_enable_reply)                                   \
+_(set_ipfix_exporter_reply)                             \
+_(set_ipfix_classify_stream_reply)                      \
+_(ipfix_classify_table_add_del_reply)                   \
 _(pg_capture_reply)                                     \
 _(pg_enable_disable_reply)                              \
 _(ip_source_and_port_range_check_add_del_reply)         \
@@ -3641,8 +3643,12 @@ _(CLASSIFY_TABLE_IDS_REPLY, classify_table_ids_reply)                   \
 _(CLASSIFY_TABLE_BY_INTERFACE_REPLY, classify_table_by_interface_reply) \
 _(CLASSIFY_TABLE_INFO_REPLY, classify_table_info_reply)                 \
 _(CLASSIFY_SESSION_DETAILS, classify_session_details)                   \
-_(IPFIX_ENABLE_REPLY, ipfix_enable_reply)                               \
-_(IPFIX_DETAILS, ipfix_details)                                         \
+_(SET_IPFIX_EXPORTER_REPLY, set_ipfix_exporter_reply)                   \
+_(IPFIX_EXPORTER_DETAILS, ipfix_exporter_details)                       \
+_(SET_IPFIX_CLASSIFY_STREAM_REPLY, set_ipfix_classify_stream_reply)     \
+_(IPFIX_CLASSIFY_STREAM_DETAILS, ipfix_classify_stream_details)         \
+_(IPFIX_CLASSIFY_TABLE_ADD_DEL_REPLY, ipfix_classify_table_add_del_reply) \
+_(IPFIX_CLASSIFY_TABLE_DETAILS, ipfix_classify_table_details)           \
 _(GET_NEXT_INDEX_REPLY, get_next_index_reply)                           \
 _(PG_CREATE_INTERFACE_REPLY, pg_create_interface_reply)                 \
 _(PG_CAPTURE_REPLY, pg_capture_reply)                                   \
@@ -8618,10 +8624,10 @@ api_classify_set_interface_l2_tables (vat_main_t * vam)
 }
 
 static int
-api_ipfix_enable (vat_main_t * vam)
+api_set_ipfix_exporter (vat_main_t * vam)
 {
   unformat_input_t *i = vam->input;
-  vl_api_ipfix_enable_t *mp;
+  vl_api_set_ipfix_exporter_t *mp;
   ip4_address_t collector_address;
   u8 collector_address_set = 0;
   u32 collector_port = ~0;
@@ -8630,6 +8636,7 @@ api_ipfix_enable (vat_main_t * vam)
   u32 vrf_id = ~0;
   u32 path_mtu = ~0;
   u32 template_interval = ~0;
+  u8 udp_checksum = 0;
   f64 timeout;
 
   while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
@@ -8648,6 +8655,8 @@ api_ipfix_enable (vat_main_t * vam)
        ;
       else if (unformat (i, "template_interval %d", &template_interval))
        ;
+      else if (unformat (i, "udp_checksum"))
+       udp_checksum = 1;
       else
        break;
     }
@@ -8664,7 +8673,7 @@ api_ipfix_enable (vat_main_t * vam)
       return -99;
     }
 
-  M (IPFIX_ENABLE, ipfix_enable);
+  M (SET_IPFIX_EXPORTER, set_ipfix_exporter);
 
   memcpy (mp->collector_address, collector_address.data,
          sizeof (collector_address.data));
@@ -8673,6 +8682,101 @@ api_ipfix_enable (vat_main_t * vam)
   mp->vrf_id = htonl (vrf_id);
   mp->path_mtu = htonl (path_mtu);
   mp->template_interval = htonl (template_interval);
+  mp->udp_checksum = udp_checksum;
+
+  S;
+  W;
+  /* NOTREACHED */
+}
+
+static int
+api_set_ipfix_classify_stream (vat_main_t * vam)
+{
+  unformat_input_t *i = vam->input;
+  vl_api_set_ipfix_classify_stream_t *mp;
+  u32 domain_id = 0;
+  u32 src_port = UDP_DST_PORT_ipfix;
+  f64 timeout;
+
+  while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (i, "domain %d", &domain_id))
+       ;
+      else if (unformat (i, "src_port %d", &src_port))
+       ;
+      else
+       {
+         errmsg ("unknown input `%U'", format_unformat_error, i);
+         return -99;
+       }
+    }
+
+  M (SET_IPFIX_CLASSIFY_STREAM, set_ipfix_classify_stream);
+
+  mp->domain_id = htonl (domain_id);
+  mp->src_port = htons ((u16) src_port);
+
+  S;
+  W;
+  /* NOTREACHED */
+}
+
+static int
+api_ipfix_classify_table_add_del (vat_main_t * vam)
+{
+  unformat_input_t *i = vam->input;
+  vl_api_ipfix_classify_table_add_del_t *mp;
+  int is_add = -1;
+  u32 classify_table_index;
+  u8 ip_version = 0;
+  u8 transport_protocol = 255;
+  f64 timeout;
+
+  while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (i, "add"))
+       is_add = 1;
+      else if (unformat (i, "del"))
+       is_add = 0;
+      else if (unformat (i, "table %d", &classify_table_index))
+       ;
+      else if (unformat (i, "ip4"))
+       ip_version = 4;
+      else if (unformat (i, "ip6"))
+       ip_version = 6;
+      else if (unformat (i, "tcp"))
+       transport_protocol = 6;
+      else if (unformat (i, "udp"))
+       transport_protocol = 17;
+      else
+       {
+         errmsg ("unknown input `%U'", format_unformat_error, i);
+         return -99;
+       }
+    }
+
+  if (is_add == -1)
+    {
+      errmsg ("expecting: add|del");
+      return -99;
+    }
+  if (classify_table_index == ~0)
+    {
+      errmsg ("classifier table not specified");
+      return -99;
+    }
+  if (ip_version == 0)
+    {
+      errmsg ("IP version not specified");
+      return -99;
+    }
+
+  M (IPFIX_CLASSIFY_TABLE_ADD_DEL, ipfix_classify_table_add_del);
+
+  mp->is_add = is_add;
+  mp->table_id = htonl (classify_table_index);
+  mp->ip_version = ip_version;
+  mp->transport_protocol = transport_protocol;
 
   S;
   W;
@@ -14743,25 +14847,26 @@ api_classify_session_dump (vat_main_t * vam)
 }
 
 static void
-vl_api_ipfix_details_t_handler (vl_api_ipfix_details_t * mp)
+vl_api_ipfix_exporter_details_t_handler (vl_api_ipfix_exporter_details_t * mp)
 {
   vat_main_t *vam = &vat_main;
 
   fformat (vam->ofp, "collector_address %U, collector_port %d, "
-          "src_address %U, fib_index %u, path_mtu %u, "
-          "template_interval %u\n",
+          "src_address %U, vrf_id %d, path_mtu %u, "
+          "template_interval %u, udp_checksum %d\n",
           format_ip4_address, mp->collector_address,
           ntohs (mp->collector_port),
           format_ip4_address, mp->src_address,
-          ntohl (mp->fib_index),
-          ntohl (mp->path_mtu), ntohl (mp->template_interval));
+          ntohl (mp->vrf_id), ntohl (mp->path_mtu),
+          ntohl (mp->template_interval), mp->udp_checksum);
 
   vam->retval = 0;
   vam->result_ready = 1;
 }
 
 static void
-vl_api_ipfix_details_t_handler_json (vl_api_ipfix_details_t * mp)
+  vl_api_ipfix_exporter_details_t_handler_json
+  (vl_api_ipfix_exporter_details_t * mp)
 {
   vat_main_t *vam = &vat_main;
   vat_json_node_t node;
@@ -14776,10 +14881,11 @@ vl_api_ipfix_details_t_handler_json (vl_api_ipfix_details_t * mp)
                            ntohs (mp->collector_port));
   clib_memcpy (&src_address, &mp->src_address, sizeof (src_address));
   vat_json_object_add_ip4 (&node, "src_address", src_address);
-  vat_json_object_add_uint (&node, "fib_index", ntohl (mp->fib_index));
+  vat_json_object_add_int (&node, "vrf_id", ntohl (mp->vrf_id));
   vat_json_object_add_uint (&node, "path_mtu", ntohl (mp->path_mtu));
   vat_json_object_add_uint (&node, "template_interval",
                            ntohl (mp->template_interval));
+  vat_json_object_add_int (&node, "udp_checksum", mp->udp_checksum);
 
   vat_json_print (vam->ofp, &node);
   vat_json_free (&node);
@@ -14788,13 +14894,13 @@ vl_api_ipfix_details_t_handler_json (vl_api_ipfix_details_t * mp)
 }
 
 int
-api_ipfix_dump (vat_main_t * vam)
+api_ipfix_exporter_dump (vat_main_t * vam)
 {
-  vl_api_ipfix_dump_t *mp;
+  vl_api_ipfix_exporter_dump_t *mp;
   f64 timeout;
 
   /* Construct the API message */
-  M (IPFIX_DUMP, ipfix_dump);
+  M (IPFIX_EXPORTER_DUMP, ipfix_exporter_dump);
   mp->context = 0;
 
   S;
@@ -14803,6 +14909,108 @@ api_ipfix_dump (vat_main_t * vam)
   return 0;
 }
 
+static int
+api_ipfix_classify_stream_dump (vat_main_t * vam)
+{
+  vl_api_ipfix_classify_stream_dump_t *mp;
+  f64 timeout;
+
+  /* Construct the API message */
+  M (IPFIX_CLASSIFY_STREAM_DUMP, ipfix_classify_stream_dump);
+  mp->context = 0;
+
+  S;
+  W;
+  /* NOTREACHED */
+  return 0;
+}
+
+static void
+  vl_api_ipfix_classify_stream_details_t_handler
+  (vl_api_ipfix_classify_stream_details_t * mp)
+{
+  vat_main_t *vam = &vat_main;
+  fformat (vam->ofp, "domain_id %d, src_port %d\n",
+          ntohl (mp->domain_id), ntohs (mp->src_port));
+  vam->retval = 0;
+  vam->result_ready = 1;
+}
+
+static void
+  vl_api_ipfix_classify_stream_details_t_handler_json
+  (vl_api_ipfix_classify_stream_details_t * mp)
+{
+  vat_main_t *vam = &vat_main;
+  vat_json_node_t node;
+
+  vat_json_init_object (&node);
+  vat_json_object_add_uint (&node, "domain_id", ntohl (mp->domain_id));
+  vat_json_object_add_uint (&node, "src_port", ntohs (mp->src_port));
+
+  vat_json_print (vam->ofp, &node);
+  vat_json_free (&node);
+  vam->retval = 0;
+  vam->result_ready = 1;
+}
+
+static int
+api_ipfix_classify_table_dump (vat_main_t * vam)
+{
+  vl_api_ipfix_classify_table_dump_t *mp;
+  f64 timeout;
+
+  if (!vam->json_output)
+    {
+      fformat (vam->ofp, "%15s%15s%20s\n", "table_id", "ip_version",
+              "transport_protocol");
+    }
+
+  /* Construct the API message */
+  M (IPFIX_CLASSIFY_TABLE_DUMP, ipfix_classify_table_dump);
+
+  /* send it... */
+  S;
+
+  /* Use a control ping for synchronization */
+  {
+    vl_api_control_ping_t *mp;
+    M (CONTROL_PING, control_ping);
+    S;
+  }
+  W;
+}
+
+static void
+  vl_api_ipfix_classify_table_details_t_handler
+  (vl_api_ipfix_classify_table_details_t * mp)
+{
+  vat_main_t *vam = &vat_main;
+  fformat (vam->ofp, "%15d%15d%20d\n", ntohl (mp->table_id), mp->ip_version,
+          mp->transport_protocol);
+}
+
+static void
+  vl_api_ipfix_classify_table_details_t_handler_json
+  (vl_api_ipfix_classify_table_details_t * mp)
+{
+  vat_json_node_t *node = NULL;
+  vat_main_t *vam = &vat_main;
+
+  if (VAT_JSON_ARRAY != vam->json_tree.type)
+    {
+      ASSERT (VAT_JSON_NONE == vam->json_tree.type);
+      vat_json_init_array (&vam->json_tree);
+    }
+
+  node = vat_json_array_add (&vam->json_tree);
+  vat_json_init_object (node);
+
+  vat_json_object_add_uint (node, "table_id", ntohl (mp->table_id));
+  vat_json_object_add_uint (node, "ip_version", mp->ip_version);
+  vat_json_object_add_uint (node, "transport_protocol",
+                           mp->transport_protocol);
+}
+
 int
 api_pg_create_interface (vat_main_t * vam)
 {
@@ -15862,10 +16070,14 @@ _(classify_table_ids, "")                                               \
 _(classify_table_by_interface, "sw_if_index <sw_if_index>")             \
 _(classify_table_info, "table_id <nn>")                                 \
 _(classify_session_dump, "table_id <nn>")                               \
-_(ipfix_enable, "collector_address <ip4> [collector_port <nn>] "        \
-                "src_address <ip4> [fib_id <nn>] [path_mtu <nn>] "      \
-                "[template_interval <nn>]")                             \
-_(ipfix_dump, "")                                                       \
+_(set_ipfix_exporter, "collector_address <ip4> [collector_port <nn>] "  \
+    "src_address <ip4> [vrf_id <nn>] [path_mtu <nn>] "                  \
+    "[template_interval <nn>] [udp_checksum]")                          \
+_(ipfix_exporter_dump, "")                                              \
+_(set_ipfix_classify_stream, "[domain <domain-id>] [src_port <src-port>]") \
+_(ipfix_classify_stream_dump, "")                                       \
+_(ipfix_classify_table_add_del, "table <table-index> ip4|ip6 [tcp|udp]")\
+_(ipfix_classify_table_dump, "")                                        \
 _(get_next_index, "node-name <node-name> next-node-name <node-name>")   \
 _(pg_create_interface, "if_id <nn>")                                    \
 _(pg_capture, "if_id <nnn> pcap <file_name> count <nnn> [disable]")     \
index c339cb4..3653243 100644 (file)
@@ -84,6 +84,7 @@
 #include <vnet/devices/netmap/netmap.h>
 #include <vnet/flow/flow_report.h>
 #include <vnet/ipsec-gre/ipsec_gre.h>
+#include <vnet/flow/flow_report_classify.h>
 
 #undef BIHASH_TYPE
 #undef __included_bihash_template_h__
@@ -391,8 +392,12 @@ _(CLASSIFY_TABLE_BY_INTERFACE, classify_table_by_interface)             \
 _(CLASSIFY_TABLE_INFO,classify_table_info)                              \
 _(CLASSIFY_SESSION_DUMP,classify_session_dump)                          \
 _(CLASSIFY_SESSION_DETAILS,classify_session_details)                    \
-_(IPFIX_ENABLE,ipfix_enable)                                            \
-_(IPFIX_DUMP,ipfix_dump)                                                \
+_(SET_IPFIX_EXPORTER, set_ipfix_exporter)                               \
+_(IPFIX_EXPORTER_DUMP, ipfix_exporter_dump)                             \
+_(SET_IPFIX_CLASSIFY_STREAM, set_ipfix_classify_stream)                 \
+_(IPFIX_CLASSIFY_STREAM_DUMP, ipfix_classify_stream_dump)               \
+_(IPFIX_CLASSIFY_TABLE_ADD_DEL, ipfix_classify_table_add_del)           \
+_(IPFIX_CLASSIFY_TABLE_DUMP, ipfix_classify_table_dump)                 \
 _(GET_NEXT_INDEX, get_next_index)                                       \
 _(PG_CREATE_INTERFACE, pg_create_interface)                             \
 _(PG_CAPTURE, pg_capture)                                               \
@@ -7928,15 +7933,16 @@ vl_api_classify_session_dump_t_handler (vl_api_classify_session_dump_t * mp)
 }
 
 static void
-vl_api_ipfix_enable_t_handler (vl_api_ipfix_enable_t * mp)
+vl_api_set_ipfix_exporter_t_handler (vl_api_set_ipfix_exporter_t * mp)
 {
   vlib_main_t *vm = vlib_get_main ();
   flow_report_main_t *frm = &flow_report_main;
-  vl_api_ipfix_enable_reply_t *rmp;
+  vl_api_set_ipfix_exporter_reply_t *rmp;
   ip4_address_t collector, src;
   u16 collector_port = UDP_DST_PORT_ipfix;
   u32 path_mtu;
   u32 template_interval;
+  u8 udp_checksum;
   u32 fib_id;
   u32 fib_index = ~0;
   int rv = 0;
@@ -7949,13 +7955,20 @@ vl_api_ipfix_enable_t_handler (vl_api_ipfix_enable_t * mp)
   fib_id = ntohl (mp->vrf_id);
 
   ip4_main_t *im = &ip4_main;
-  uword *p = hash_get (im->fib_index_by_table_id, fib_id);
-  if (!p)
+  if (fib_id == ~0)
     {
-      rv = VNET_API_ERROR_NO_SUCH_FIB;
-      goto out;
+      fib_index = ~0;
+    }
+  else
+    {
+      uword *p = hash_get (im->fib_index_by_table_id, fib_id);
+      if (!p)
+       {
+         rv = VNET_API_ERROR_NO_SUCH_FIB;
+         goto out;
+       }
+      fib_index = p[0];
     }
-  fib_index = p[0];
 
   path_mtu = ntohl (mp->path_mtu);
   if (path_mtu == ~0)
@@ -7963,6 +7976,7 @@ vl_api_ipfix_enable_t_handler (vl_api_ipfix_enable_t * mp)
   template_interval = ntohl (mp->template_interval);
   if (template_interval == ~0)
     template_interval = 20;
+  udp_checksum = mp->udp_checksum;
 
   if (collector.as_u32 == 0)
     {
@@ -8000,20 +8014,23 @@ vl_api_ipfix_enable_t_handler (vl_api_ipfix_enable_t * mp)
   frm->fib_index = fib_index;
   frm->path_mtu = path_mtu;
   frm->template_interval = template_interval;
+  frm->udp_checksum = udp_checksum;
 
   /* Turn on the flow reporting process */
   vlib_process_signal_event (vm, flow_report_process_node.index, 1, 0);
 
 out:
-  REPLY_MACRO (VL_API_IPFIX_ENABLE_REPLY);
+  REPLY_MACRO (VL_API_SET_IPFIX_EXPORTER_REPLY);
 }
 
 static void
-vl_api_ipfix_dump_t_handler (vl_api_ipfix_dump_t * mp)
+vl_api_ipfix_exporter_dump_t_handler (vl_api_ipfix_exporter_dump_t * mp)
 {
   flow_report_main_t *frm = &flow_report_main;
   unix_shared_memory_queue_t *q;
-  vl_api_ipfix_details_t *rmp;
+  vl_api_ipfix_exporter_details_t *rmp;
+  ip4_main_t *im = &ip4_main;
+  u32 vrf_id;
 
   q = vl_api_client_index_to_input_queue (mp->client_index);
   if (!q)
@@ -8021,20 +8038,196 @@ vl_api_ipfix_dump_t_handler (vl_api_ipfix_dump_t * mp)
 
   rmp = vl_msg_api_alloc (sizeof (*rmp));
   memset (rmp, 0, sizeof (*rmp));
-  rmp->_vl_msg_id = ntohs (VL_API_IPFIX_DETAILS);
+  rmp->_vl_msg_id = ntohs (VL_API_IPFIX_EXPORTER_DETAILS);
   rmp->context = mp->context;
   memcpy (rmp->collector_address, frm->ipfix_collector.data,
          sizeof (frm->ipfix_collector.data));
   rmp->collector_port = htons (frm->collector_port);
   memcpy (rmp->src_address, frm->src_address.data,
          sizeof (frm->src_address.data));
-  rmp->fib_index = htonl (frm->fib_index);
+  if (frm->fib_index == ~0)
+    vrf_id = ~0;
+  else
+    vrf_id = im->fibs[frm->fib_index].table_id;
+  rmp->vrf_id = htonl (vrf_id);
   rmp->path_mtu = htonl (frm->path_mtu);
   rmp->template_interval = htonl (frm->template_interval);
+  rmp->udp_checksum = (frm->udp_checksum != 0);
+
+  vl_msg_api_send_shmem (q, (u8 *) & rmp);
+}
+
+static void
+  vl_api_set_ipfix_classify_stream_t_handler
+  (vl_api_set_ipfix_classify_stream_t * mp)
+{
+  vl_api_set_ipfix_classify_stream_reply_t *rmp;
+  flow_report_classify_main_t *fcm = &flow_report_classify_main;
+  flow_report_main_t *frm = &flow_report_main;
+  u32 domain_id = 0;
+  u32 src_port = UDP_DST_PORT_ipfix;
+  int rv = 0;
+
+  domain_id = ntohl (mp->domain_id);
+  src_port = ntohs (mp->src_port);
+
+  if (fcm->src_port != 0 &&
+      (fcm->domain_id != domain_id || fcm->src_port != (u16) src_port))
+    {
+      int rv = vnet_stream_change (frm, fcm->domain_id, fcm->src_port,
+                                  domain_id, (u16) src_port);
+      ASSERT (rv == 0);
+    }
+
+  fcm->domain_id = domain_id;
+  fcm->src_port = (u16) src_port;
+
+  REPLY_MACRO (VL_API_SET_IPFIX_CLASSIFY_STREAM_REPLY);
+}
+
+static void
+  vl_api_ipfix_classify_stream_dump_t_handler
+  (vl_api_ipfix_classify_stream_dump_t * mp)
+{
+  flow_report_classify_main_t *fcm = &flow_report_classify_main;
+  unix_shared_memory_queue_t *q;
+  vl_api_ipfix_classify_stream_details_t *rmp;
+
+  q = vl_api_client_index_to_input_queue (mp->client_index);
+
+  rmp = vl_msg_api_alloc (sizeof (*rmp));
+  memset (rmp, 0, sizeof (*rmp));
+  rmp->_vl_msg_id = ntohs (VL_API_IPFIX_CLASSIFY_STREAM_DETAILS);
+  rmp->context = mp->context;
+  rmp->domain_id = htonl (fcm->domain_id);
+  rmp->src_port = htons (fcm->src_port);
 
   vl_msg_api_send_shmem (q, (u8 *) & rmp);
 }
 
+static void
+  vl_api_ipfix_classify_table_add_del_t_handler
+  (vl_api_ipfix_classify_table_add_del_t * mp)
+{
+  vl_api_ipfix_classify_table_add_del_reply_t *rmp;
+  flow_report_classify_main_t *fcm = &flow_report_classify_main;
+  flow_report_main_t *frm = &flow_report_main;
+  vnet_flow_report_add_del_args_t args;
+  ipfix_classify_table_t *table;
+  int is_add;
+  u32 classify_table_index;
+  u8 ip_version;
+  u8 transport_protocol;
+  int rv = 0;
+
+  classify_table_index = ntohl (mp->table_id);
+  ip_version = mp->ip_version;
+  transport_protocol = mp->transport_protocol;
+  is_add = mp->is_add;
+
+  if (fcm->src_port == 0)
+    {
+      /* call set_ipfix_classify_stream first */
+      rv = VNET_API_ERROR_UNSPECIFIED;
+      goto out;
+    }
+
+  memset (&args, 0, sizeof (args));
+
+  table = 0;
+  int i;
+  for (i = 0; i < vec_len (fcm->tables); i++)
+    if (ipfix_classify_table_index_valid (i))
+      if (fcm->tables[i].classify_table_index == classify_table_index)
+       {
+         table = &fcm->tables[i];
+         break;
+       }
+
+  if (is_add)
+    {
+      if (table)
+       {
+         rv = VNET_API_ERROR_VALUE_EXIST;
+         goto out;
+       }
+      table = ipfix_classify_add_table ();
+      table->classify_table_index = classify_table_index;
+    }
+  else
+    {
+      if (!table)
+       {
+         rv = VNET_API_ERROR_NO_SUCH_ENTRY;
+         goto out;
+       }
+    }
+
+  table->ip_version = ip_version;
+  table->transport_protocol = transport_protocol;
+
+  args.opaque.as_uword = table - fcm->tables;
+  args.rewrite_callback = ipfix_classify_template_rewrite;
+  args.flow_data_callback = ipfix_classify_send_flows;
+  args.is_add = is_add;
+  args.domain_id = fcm->domain_id;
+  args.src_port = fcm->src_port;
+
+  rv = vnet_flow_report_add_del (frm, &args);
+  if (rv != 0)
+    goto out;
+
+  if (is_add)
+    {
+      if (rv != 0)
+       ipfix_classify_delete_table (table - fcm->tables);
+    }
+  else
+    {
+      if (rv == 0)
+       ipfix_classify_delete_table (table - fcm->tables);
+    }
+
+out:
+  REPLY_MACRO (VL_API_SET_IPFIX_CLASSIFY_STREAM_REPLY);
+}
+
+static void
+send_ipfix_classify_table_details (u32 table_index,
+                                  unix_shared_memory_queue_t * q,
+                                  u32 context)
+{
+  flow_report_classify_main_t *fcm = &flow_report_classify_main;
+  vl_api_ipfix_classify_table_details_t *mp;
+
+  ipfix_classify_table_t *table = &fcm->tables[table_index];
+
+  mp = vl_msg_api_alloc (sizeof (*mp));
+  memset (mp, 0, sizeof (*mp));
+  mp->_vl_msg_id = ntohs (VL_API_IPFIX_CLASSIFY_TABLE_DETAILS);
+  mp->context = context;
+  mp->table_id = htonl (table->classify_table_index);
+  mp->ip_version = table->ip_version;
+  mp->transport_protocol = table->transport_protocol;
+
+  vl_msg_api_send_shmem (q, (u8 *) & mp);
+}
+
+static void
+  vl_api_ipfix_classify_table_dump_t_handler
+  (vl_api_ipfix_classify_table_dump_t * mp)
+{
+  flow_report_classify_main_t *fcm = &flow_report_classify_main;
+  unix_shared_memory_queue_t *q;
+  u32 i;
+
+  q = vl_api_client_index_to_input_queue (mp->client_index);
+
+  for (i = 0; i < vec_len (fcm->tables); i++)
+    if (ipfix_classify_table_index_valid (i))
+      send_ipfix_classify_table_details (i, q, mp->context);
+}
+
 static void
 vl_api_pg_create_interface_t_handler (vl_api_pg_create_interface_t * mp)
 {
index 5ca7ccc..e8ee551 100644 (file)
@@ -2093,12 +2093,12 @@ static void *vl_api_classify_session_dump_t_print
   FINISH;
 }
 
-static void *vl_api_ipfix_enable_t_print
-  (vl_api_ipfix_enable_t * mp, void *handle)
+static void *vl_api_set_ipfix_exporter_t_print
+  (vl_api_set_ipfix_exporter_t * mp, void *handle)
 {
   u8 *s;
 
-  s = format (0, "SCRIPT: ipfix_enable ");
+  s = format (0, "SCRIPT: set_ipfix_exporter ");
 
   s = format (s, "collector-address %U ", format_ip4_address,
              (ip4_address_t *) mp->collector_address);
@@ -2108,16 +2108,64 @@ static void *vl_api_ipfix_enable_t_print
   s = format (s, "vrf-id %d ", ntohl (mp->vrf_id));
   s = format (s, "path-mtu %d ", ntohl (mp->path_mtu));
   s = format (s, "template-interval %d ", ntohl (mp->template_interval));
+  s = format (s, "udp-checksum %d ", mp->udp_checksum);
 
   FINISH;
 }
 
-static void *vl_api_ipfix_dump_t_print
-  (vl_api_ipfix_dump_t * mp, void *handle)
+static void *vl_api_ipfix_exporter_dump_t_print
+  (vl_api_ipfix_exporter_dump_t * mp, void *handle)
 {
   u8 *s;
 
-  s = format (0, "SCRIPT: ipfix_dump ");
+  s = format (0, "SCRIPT: ipfix_exporter_dump ");
+
+  FINISH;
+}
+
+static void *vl_api_set_ipfix_classify_stream_t_print
+  (vl_api_set_ipfix_classify_stream_t * mp, void *handle)
+{
+  u8 *s;
+
+  s = format (0, "SCRIPT: set_ipfix_classify_stream ");
+
+  s = format (s, "domain-id %d ", ntohl (mp->domain_id));
+  s = format (s, "src-port %d ", ntohs (mp->src_port));
+
+  FINISH;
+}
+
+static void *vl_api_ipfix_classify_stream_dump_t_print
+  (vl_api_ipfix_classify_stream_dump_t * mp, void *handle)
+{
+  u8 *s;
+
+  s = format (0, "SCRIPT: ipfix_classify_stream_dump ");
+
+  FINISH;
+}
+
+static void *vl_api_ipfix_classify_table_add_del_t_print
+  (vl_api_ipfix_classify_table_add_del_t * mp, void *handle)
+{
+  u8 *s;
+
+  s = format (0, "SCRIPT: ipfix_classify_table_add_del ");
+
+  s = format (s, "table-id %d ", ntohl (mp->table_id));
+  s = format (s, "ip-version %d ", mp->ip_version);
+  s = format (s, "transport-protocol %d ", mp->transport_protocol);
+
+  FINISH;
+}
+
+static void *vl_api_ipfix_classify_table_dump_t_print
+  (vl_api_ipfix_classify_table_dump_t * mp, void *handle)
+{
+  u8 *s;
+
+  s = format (0, "SCRIPT: ipfix_classify_table_dump ");
 
   FINISH;
 }
@@ -2733,8 +2781,12 @@ _(CLASSIFY_TABLE_IDS,classify_table_ids)                                \
 _(CLASSIFY_TABLE_BY_INTERFACE, classify_table_by_interface)             \
 _(CLASSIFY_TABLE_INFO,classify_table_info)                              \
 _(CLASSIFY_SESSION_DUMP,classify_session_dump)                          \
-_(IPFIX_ENABLE,ipfix_enable)                                            \
-_(IPFIX_DUMP,ipfix_dump)                                                \
+_(SET_IPFIX_EXPORTER, set_ipfix_exporter)                               \
+_(IPFIX_EXPORTER_DUMP, ipfix_exporter_dump)                             \
+_(SET_IPFIX_CLASSIFY_STREAM, set_ipfix_classify_stream)                 \
+_(IPFIX_CLASSIFY_STREAM_DUMP, ipfix_classify_stream_dump)               \
+_(IPFIX_CLASSIFY_TABLE_ADD_DEL, ipfix_classify_table_add_del)           \
+_(IPFIX_CLASSIFY_TABLE_DUMP, ipfix_classify_table_dump)                 \
 _(GET_NEXT_INDEX, get_next_index)                                       \
 _(PG_CREATE_INTERFACE,pg_create_interface)                              \
 _(PG_CAPTURE, pg_capture)                                               \
index 5c1502a..11bb30c 100644 (file)
@@ -4570,17 +4570,18 @@ define classify_session_details
   u8 match[match_length];
 };
 
-/** \brief Enable and configure IPFIX exporter process request
+/** \brief Configure IPFIX exporter process request
     @param client_index - opaque cookie to identify the sender
     @param context - sender context, to match reply w/ request
     @param collector_address - address of IPFIX collector
-    @param collector_port - port of IPFIX 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_enable
+define set_ipfix_exporter
 {
   u32 client_index;
   u32 context;
@@ -4590,45 +4591,136 @@ define ipfix_enable
   u32 vrf_id;
   u32 path_mtu;
   u32 template_interval;
+  u8 udp_checksum;
 };
 
-/** \brief Reply to IPFIX enable and configure request
+/** \brief Reply to IPFIX exporter configure request
     @param context - sender context which was passed in the request
 */
-define ipfix_enable_reply
+define set_ipfix_exporter_reply
 {
   u32 context;
   i32 retval;
 };
 
-/** \brief IPFIX dump request
+/** \brief IPFIX exporter dump request
     @param client_index - opaque cookie to identify the sender
     @param context - sender context, to match reply w/ request
 */
-define ipfix_dump
+define ipfix_exporter_dump
 {
   u32 client_index;
   u32 context;
 };
 
-/** \brief Reply to IPFIX dump request
+/** \brief Reply to IPFIX exporter dump request
     @param context - sender context which was passed in the request
     @param collector_address - address of IPFIX collector
-    @param collector_port - port of IPFIX IPFIX collector
+    @param collector_port - port of IPFIX collector
     @param src_address - address of IPFIX exporter
     @param fib_index - fib table index
     @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_details
+define ipfix_exporter_details
 {
   u32 context;
   u8 collector_address[16];
   u16 collector_port;
   u8 src_address[16];
-  u32 fib_index;
+  u32 vrf_id;
   u32 path_mtu;
   u32 template_interval;
+  u8 udp_checksum;
+};
+
+/** \brief IPFIX classify stream configure request
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param domain_id - domain ID reported in IPFIX messages for classify stream
+    @param src_port - source port of UDP session for classify stream
+*/
+define set_ipfix_classify_stream {
+    u32 client_index;
+    u32 context;
+    u32 domain_id;
+    u16 src_port;
+};
+
+/** \brief IPFIX classify stream configure response
+    @param context - sender context, to match reply w/ request
+    @param retval - return value for request
+*/
+define set_ipfix_classify_stream_reply {
+    u32 context;
+    i32 retval;
+};
+
+/** \brief IPFIX classify stream dump request
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+*/
+define ipfix_classify_stream_dump {
+    u32 client_index;
+    u32 context;
+};
+
+/** \brief Reply to IPFIX classify stream dump request
+    @param context - sender context, to match reply w/ request
+    @param domain_id - domain ID reported in IPFIX messages for classify stream
+    @param src_port - source port of UDP session for classify stream
+*/
+define ipfix_classify_stream_details {
+    u32 context;
+    u32 domain_id;
+    u16 src_port;
+};
+
+/** \brief IPFIX add or delete classifier table request
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param table_id - classifier table ID
+    @param ip_version - version of IP used in the classifier table
+    @param transport_protocol - transport protocol used in the classifier table or 255 for unspecified
+*/
+define ipfix_classify_table_add_del {
+    u32 client_index;
+    u32 context;
+    u32 table_id;
+    u8 ip_version;
+    u8 transport_protocol;
+    u8 is_add;
+};
+
+/** \brief IPFIX add classifier table response
+    @param context - sender context which was passed in the request
+*/
+define ipfix_classify_table_add_del_reply {
+    u32 context;
+    i32 retval;
+};
+
+/** \brief IPFIX classify tables dump request
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+*/
+define ipfix_classify_table_dump {
+    u32 client_index;
+    u32 context;
+};
+
+/** \brief Reply to IPFIX classify tables dump request
+    @param context - sender context, to match reply w/ request
+    @param table_id - classifier table ID
+    @param ip_version - version of IP used in the classifier table
+    @param transport_protocol - transport protocol used in the classifier table or 255 for unspecified
+*/
+define ipfix_classify_table_details {
+    u32 context;
+    u32 table_id;
+    u8 ip_version;
+    u8 transport_protocol;
 };
 
 /** \brief Query relative index via node names