flowprobe: add support for reporting on inbound packets
[vpp.git] / src / plugins / flowprobe / flowprobe.c
index 0b07021..5a747a6 100644 (file)
@@ -26,6 +26,7 @@
 #include <vnet/vnet.h>
 #include <vpp/app/version.h>
 #include <vnet/plugin/plugin.h>
+#include <vnet/udp/udp_local.h>
 #include <flowprobe/flowprobe.h>
 
 #include <vlibapi/api.h>
@@ -45,25 +46,47 @@ uword flowprobe_walker_process (vlib_main_t * vm, vlib_node_runtime_t * rt,
 
 /* Define the per-interface configurable features */
 /* *INDENT-OFF* */
-VNET_FEATURE_INIT (flow_perpacket_ip4, static) =
-{
+VNET_FEATURE_INIT (flowprobe_input_ip4_unicast, static) = {
+  .arc_name = "ip4-unicast",
+  .node_name = "flowprobe-input-ip4",
+  .runs_before = VNET_FEATURES ("ip4-lookup"),
+};
+VNET_FEATURE_INIT (flowprobe_input_ip4_multicast, static) = {
+  .arc_name = "ip4-multicast",
+  .node_name = "flowprobe-input-ip4",
+  .runs_before = VNET_FEATURES ("ip4-mfib-forward-lookup"),
+};
+VNET_FEATURE_INIT (flowprobe_input_ip6_unicast, static) = {
+  .arc_name = "ip6-unicast",
+  .node_name = "flowprobe-input-ip6",
+  .runs_before = VNET_FEATURES ("ip6-lookup"),
+};
+VNET_FEATURE_INIT (flowprobe_input_ip6_multicast, static) = {
+  .arc_name = "ip6-multicast",
+  .node_name = "flowprobe-input-ip6",
+  .runs_before = VNET_FEATURES ("ip6-mfib-forward-lookup"),
+};
+VNET_FEATURE_INIT (flowprobe_input_l2, static) = {
+  .arc_name = "device-input",
+  .node_name = "flowprobe-input-l2",
+  .runs_before = VNET_FEATURES ("ethernet-input"),
+};
+VNET_FEATURE_INIT (flowprobe_output_ip4, static) = {
   .arc_name = "ip4-output",
-  .node_name = "flowprobe-ip4",
+  .node_name = "flowprobe-output-ip4",
   .runs_before = VNET_FEATURES ("interface-output"),
 };
 
-VNET_FEATURE_INIT (flow_perpacket_ip6, static) =
-{
+VNET_FEATURE_INIT (flowprobe_output_ip6, static) = {
   .arc_name = "ip6-output",
-  .node_name = "flowprobe-ip6",
+  .node_name = "flowprobe-output-ip6",
   .runs_before = VNET_FEATURES ("interface-output"),
 };
 
-VNET_FEATURE_INIT (flow_perpacket_l2, static) =
-{
+VNET_FEATURE_INIT (flowprobe_output_l2, static) = {
   .arc_name = "interface-output",
-  .node_name = "flowprobe-l2",
-  .runs_before = VNET_FEATURES ("interface-tx"),
+  .node_name = "flowprobe-output-l2",
+  .runs_before = VNET_FEATURES ("interface-output-arc-end"),
 };
 /* *INDENT-ON* */
 
@@ -143,7 +166,7 @@ flowprobe_template_l2_fields (ipfix_field_specifier_t * f)
 static inline ipfix_field_specifier_t *
 flowprobe_template_common_fields (ipfix_field_specifier_t * f)
 {
-#define flowprobe_template_common_field_count() 5
+#define flowprobe_template_common_field_count() 6
   /* ingressInterface, TLV type 10, u32 */
   f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
                                      ingressInterface, 4);
@@ -154,6 +177,10 @@ flowprobe_template_common_fields (ipfix_field_specifier_t * f)
                                      egressInterface, 4);
   f++;
 
+  /* flowDirection, TLV type 61, u8 */
+  f->e_id_length = ipfix_e_id_length (0 /* enterprise */, flowDirection, 1);
+  f++;
+
   /* packetDeltaCount, TLV type 2, u64 */
   f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
                                      packetDeltaCount, 8);
@@ -202,10 +229,7 @@ flowprobe_template_l4_fields (ipfix_field_specifier_t * f)
  * @returns u8 * vector containing the indicated IPFIX template packet
  */
 static inline u8 *
-flowprobe_template_rewrite_inline (flow_report_main_t * frm,
-                                  flow_report_t * fr,
-                                  ip4_address_t * collector_address,
-                                  ip4_address_t * src_address,
+flowprobe_template_rewrite_inline (ipfix_exporter_t *exp, flow_report_t *fr,
                                   u16 collector_port,
                                   flowprobe_variant_t which)
 {
@@ -224,7 +248,7 @@ flowprobe_template_rewrite_inline (flow_report_main_t * frm,
   flowprobe_record_t flags = fr->opaque.as_uword;
   bool collect_ip4 = false, collect_ip6 = false;
 
-  stream = &frm->streams[fr->stream_index];
+  stream = &exp->streams[fr->stream_index];
 
   if (flags & FLOW_RECORD_L3)
     {
@@ -263,8 +287,8 @@ flowprobe_template_rewrite_inline (flow_report_main_t * frm,
   ip->ip_version_and_header_length = 0x45;
   ip->ttl = 254;
   ip->protocol = IP_PROTOCOL_UDP;
-  ip->src_address.as_u32 = src_address->as_u32;
-  ip->dst_address.as_u32 = collector_address->as_u32;
+  ip->src_address.as_u32 = exp->src_address.ip.ip4.as_u32;
+  ip->dst_address.as_u32 = exp->ipfix_collector.ip.ip4.as_u32;
   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));
@@ -309,73 +333,53 @@ flowprobe_template_rewrite_inline (flow_report_main_t * frm,
 }
 
 static u8 *
-flowprobe_template_rewrite_ip6 (flow_report_main_t * frm,
-                               flow_report_t * fr,
-                               ip4_address_t * collector_address,
-                               ip4_address_t * src_address,
+flowprobe_template_rewrite_ip6 (ipfix_exporter_t *exp, flow_report_t *fr,
                                u16 collector_port,
-                               ipfix_report_element_t * elts,
-                               u32 n_elts, u32 * stream_index)
+                               ipfix_report_element_t *elts, u32 n_elts,
+                               u32 *stream_index)
 {
-  return flowprobe_template_rewrite_inline
-    (frm, fr, collector_address, src_address, collector_port,
-     FLOW_VARIANT_IP6);
+  return flowprobe_template_rewrite_inline (exp, fr, collector_port,
+                                           FLOW_VARIANT_IP6);
 }
 
 static u8 *
-flowprobe_template_rewrite_ip4 (flow_report_main_t * frm,
-                               flow_report_t * fr,
-                               ip4_address_t * collector_address,
-                               ip4_address_t * src_address,
+flowprobe_template_rewrite_ip4 (ipfix_exporter_t *exp, flow_report_t *fr,
                                u16 collector_port,
-                               ipfix_report_element_t * elts,
-                               u32 n_elts, u32 * stream_index)
+                               ipfix_report_element_t *elts, u32 n_elts,
+                               u32 *stream_index)
 {
-  return flowprobe_template_rewrite_inline
-    (frm, fr, collector_address, src_address, collector_port,
-     FLOW_VARIANT_IP4);
+  return flowprobe_template_rewrite_inline (exp, fr, collector_port,
+                                           FLOW_VARIANT_IP4);
 }
 
 static u8 *
-flowprobe_template_rewrite_l2 (flow_report_main_t * frm,
-                              flow_report_t * fr,
-                              ip4_address_t * collector_address,
-                              ip4_address_t * src_address,
+flowprobe_template_rewrite_l2 (ipfix_exporter_t *exp, flow_report_t *fr,
                               u16 collector_port,
-                              ipfix_report_element_t * elts,
-                              u32 n_elts, u32 * stream_index)
+                              ipfix_report_element_t *elts, u32 n_elts,
+                              u32 *stream_index)
 {
-  return flowprobe_template_rewrite_inline
-    (frm, fr, collector_address, src_address, collector_port,
-     FLOW_VARIANT_L2);
+  return flowprobe_template_rewrite_inline (exp, fr, collector_port,
+                                           FLOW_VARIANT_L2);
 }
 
 static u8 *
-flowprobe_template_rewrite_l2_ip4 (flow_report_main_t * frm,
-                                  flow_report_t * fr,
-                                  ip4_address_t * collector_address,
-                                  ip4_address_t * src_address,
+flowprobe_template_rewrite_l2_ip4 (ipfix_exporter_t *exp, flow_report_t *fr,
                                   u16 collector_port,
-                                  ipfix_report_element_t * elts,
-                                  u32 n_elts, u32 * stream_index)
+                                  ipfix_report_element_t *elts, u32 n_elts,
+                                  u32 *stream_index)
 {
-  return flowprobe_template_rewrite_inline
-    (frm, fr, collector_address, src_address, collector_port,
-     FLOW_VARIANT_L2_IP4);
+  return flowprobe_template_rewrite_inline (exp, fr, collector_port,
+                                           FLOW_VARIANT_L2_IP4);
 }
 
 static u8 *
-flowprobe_template_rewrite_l2_ip6 (flow_report_main_t * frm,
-                                  flow_report_t * fr,
-                                  ip4_address_t * collector_address,
-                                  ip4_address_t * src_address,
+flowprobe_template_rewrite_l2_ip6 (ipfix_exporter_t *exp, flow_report_t *fr,
                                   u16 collector_port,
-                                  ipfix_report_element_t * elts,
-                                  u32 n_elts, u32 * stream_index)
+                                  ipfix_report_element_t *elts, u32 n_elts,
+                                  u32 *stream_index)
 {
-  return flowprobe_template_rewrite_inline
-    (frm, fr, collector_address, src_address, collector_port,
-     FLOW_VARIANT_L2_IP6);
+  return flowprobe_template_rewrite_inline (exp, fr, collector_port,
+                                           FLOW_VARIANT_L2_IP6);
 }
 
 /**
@@ -389,27 +393,27 @@ flowprobe_template_rewrite_l2_ip6 (flow_report_main_t * frm,
  * will be sent.
  */
 vlib_frame_t *
-flowprobe_data_callback_ip4 (flow_report_main_t * frm,
-                            flow_report_t * fr,
-                            vlib_frame_t * f, u32 * to_next, u32 node_index)
+flowprobe_data_callback_ip4 (flow_report_main_t *frm, ipfix_exporter_t *exp,
+                            flow_report_t *fr, vlib_frame_t *f, u32 *to_next,
+                            u32 node_index)
 {
   flowprobe_flush_callback_ip4 ();
   return f;
 }
 
 vlib_frame_t *
-flowprobe_data_callback_ip6 (flow_report_main_t * frm,
-                            flow_report_t * fr,
-                            vlib_frame_t * f, u32 * to_next, u32 node_index)
+flowprobe_data_callback_ip6 (flow_report_main_t *frm, ipfix_exporter_t *exp,
+                            flow_report_t *fr, vlib_frame_t *f, u32 *to_next,
+                            u32 node_index)
 {
   flowprobe_flush_callback_ip6 ();
   return f;
 }
 
 vlib_frame_t *
-flowprobe_data_callback_l2 (flow_report_main_t * frm,
-                           flow_report_t * fr,
-                           vlib_frame_t * f, u32 * to_next, u32 node_index)
+flowprobe_data_callback_l2 (flow_report_main_t *frm, ipfix_exporter_t *exp,
+                           flow_report_t *fr, vlib_frame_t *f, u32 *to_next,
+                           u32 node_index)
 {
   flowprobe_flush_callback_l2 ();
   return f;
@@ -422,7 +426,7 @@ flowprobe_template_add_del (u32 domain_id, u16 src_port,
                            vnet_flow_rewrite_callback_t * rewrite_callback,
                            bool is_add, u16 * template_id)
 {
-  flow_report_main_t *frm = &flow_report_main;
+  ipfix_exporter_t *exp = &flow_report_main.exporters[0];
   vnet_flow_report_add_del_args_t a = {
     .rewrite_callback = rewrite_callback,
     .flow_data_callback = flow_data_callback,
@@ -431,7 +435,7 @@ flowprobe_template_add_del (u32 domain_id, u16 src_port,
     .src_port = src_port,
     .opaque.as_uword = flags,
   };
-  return vnet_flow_report_add_del (frm, &a, template_id);
+  return vnet_flow_report_add_del (exp, &a, template_id);
 }
 
 static void
@@ -506,6 +510,7 @@ validate_feature_on_interface (flowprobe_main_t * fm, u32 sw_if_index,
                               u8 which)
 {
   vec_validate_init_empty (fm->flow_per_interface, sw_if_index, ~0);
+  vec_validate_init_empty (fm->direction_per_interface, sw_if_index, ~0);
 
   if (fm->flow_per_interface[sw_if_index] == (u8) ~ 0)
     return -1;
@@ -519,13 +524,15 @@ validate_feature_on_interface (flowprobe_main_t * fm, u32 sw_if_index,
  * @brief configure / deconfigure the IPFIX flow-per-packet
  * @param fm flowprobe_main_t * fm
  * @param sw_if_index u32 the desired interface
+ * @param which u8 the desired datapath
+ * @param direction u8 the desired direction
  * @param is_add int 1 to enable the feature, 0 to disable it
  * @returns 0 if successful, non-zero otherwise
  */
 
 static int
-flowprobe_tx_interface_add_del_feature (flowprobe_main_t * fm,
-                                       u32 sw_if_index, u8 which, int is_add)
+flowprobe_interface_add_del_feature (flowprobe_main_t *fm, u32 sw_if_index,
+                                    u8 which, u8 direction, int is_add)
 {
   vlib_main_t *vm = vlib_get_main ();
   int rv = 0;
@@ -533,6 +540,7 @@ flowprobe_tx_interface_add_del_feature (flowprobe_main_t * fm,
   flowprobe_record_t flags = fm->record;
 
   fm->flow_per_interface[sw_if_index] = (is_add) ? which : (u8) ~ 0;
+  fm->direction_per_interface[sw_if_index] = (is_add) ? direction : (u8) ~0;
   fm->template_per_flow[which] += (is_add) ? 1 : -1;
   if (is_add && fm->template_per_flow[which] > 1)
     template_id = fm->template_reports[flags];
@@ -597,15 +605,39 @@ flowprobe_tx_interface_add_del_feature (flowprobe_main_t * fm,
       fm->template_reports[flags] = (is_add) ? template_id : 0;
     }
 
-  if (which == FLOW_VARIANT_IP4)
-    vnet_feature_enable_disable ("ip4-output", "flowprobe-ip4",
-                                sw_if_index, is_add, 0, 0);
-  else if (which == FLOW_VARIANT_IP6)
-    vnet_feature_enable_disable ("ip6-output", "flowprobe-ip6",
-                                sw_if_index, is_add, 0, 0);
-  else if (which == FLOW_VARIANT_L2)
-    vnet_feature_enable_disable ("interface-output", "flowprobe-l2",
-                                sw_if_index, is_add, 0, 0);
+  if (direction == FLOW_DIRECTION_RX || direction == FLOW_DIRECTION_BOTH)
+    {
+      if (which == FLOW_VARIANT_IP4)
+       {
+         vnet_feature_enable_disable ("ip4-unicast", "flowprobe-input-ip4",
+                                      sw_if_index, is_add, 0, 0);
+         vnet_feature_enable_disable ("ip4-multicast", "flowprobe-input-ip4",
+                                      sw_if_index, is_add, 0, 0);
+       }
+      else if (which == FLOW_VARIANT_IP6)
+       {
+         vnet_feature_enable_disable ("ip6-unicast", "flowprobe-input-ip6",
+                                      sw_if_index, is_add, 0, 0);
+         vnet_feature_enable_disable ("ip6-multicast", "flowprobe-input-ip6",
+                                      sw_if_index, is_add, 0, 0);
+       }
+      else if (which == FLOW_VARIANT_L2)
+       vnet_feature_enable_disable ("device-input", "flowprobe-input-l2",
+                                    sw_if_index, is_add, 0, 0);
+    }
+
+  if (direction == FLOW_DIRECTION_TX || direction == FLOW_DIRECTION_BOTH)
+    {
+      if (which == FLOW_VARIANT_IP4)
+       vnet_feature_enable_disable ("ip4-output", "flowprobe-output-ip4",
+                                    sw_if_index, is_add, 0, 0);
+      else if (which == FLOW_VARIANT_IP6)
+       vnet_feature_enable_disable ("ip6-output", "flowprobe-output-ip6",
+                                    sw_if_index, is_add, 0, 0);
+      else if (which == FLOW_VARIANT_L2)
+       vnet_feature_enable_disable ("interface-output", "flowprobe-output-l2",
+                                    sw_if_index, is_add, 0, 0);
+    }
 
   /* Stateful flow collection */
   if (is_add && !fm->initialized)
@@ -632,13 +664,6 @@ void vl_api_flowprobe_tx_interface_add_del_t_handler
 
   VALIDATE_SW_IF_INDEX (mp);
 
-  if (mp->which != FLOW_VARIANT_IP4 && mp->which != FLOW_VARIANT_L2
-      && mp->which != FLOW_VARIANT_IP6)
-    {
-      rv = VNET_API_ERROR_UNIMPLEMENTED;
-      goto out;
-    }
-
   if (fm->record == 0)
     {
       clib_warning ("Please specify flowprobe params record first...");
@@ -653,8 +678,8 @@ void vl_api_flowprobe_tx_interface_add_del_t_handler
       goto out;
     }
 
-  rv = flowprobe_tx_interface_add_del_feature
-    (fm, sw_if_index, mp->which, mp->is_add);
+  rv = flowprobe_interface_add_del_feature (fm, sw_if_index, mp->which,
+                                           FLOW_DIRECTION_TX, mp->is_add);
 
 out:
   BAD_SW_IF_INDEX_LABEL;
@@ -662,22 +687,89 @@ out:
   REPLY_MACRO (VL_API_FLOWPROBE_TX_INTERFACE_ADD_DEL_REPLY);
 }
 
-/**
- * @brief API message custom-dump function
- * @param mp vl_api_flowprobe_tx_interface_add_del_t * mp the api message
- * @param handle void * print function handle
- * @returns u8 * output string
- */
-static void *vl_api_flowprobe_tx_interface_add_del_t_print
-  (vl_api_flowprobe_tx_interface_add_del_t * mp, void *handle)
+void
+vl_api_flowprobe_interface_add_del_t_handler (
+  vl_api_flowprobe_interface_add_del_t *mp)
 {
-  u8 *s;
+  flowprobe_main_t *fm = &flowprobe_main;
+  vl_api_flowprobe_interface_add_del_reply_t *rmp;
+  u32 sw_if_index;
+  u8 which;
+  u8 direction;
+  bool is_add;
+  int rv = 0;
+
+  VALIDATE_SW_IF_INDEX (mp);
+
+  sw_if_index = ntohl (mp->sw_if_index);
+  is_add = mp->is_add;
+
+  if (mp->which == FLOWPROBE_WHICH_IP4)
+    which = FLOW_VARIANT_IP4;
+  else if (mp->which == FLOWPROBE_WHICH_IP6)
+    which = FLOW_VARIANT_IP6;
+  else if (mp->which == FLOWPROBE_WHICH_L2)
+    which = FLOW_VARIANT_L2;
+  else
+    {
+      clib_warning ("Invalid value of which");
+      rv = VNET_API_ERROR_INVALID_VALUE;
+      goto out;
+    }
+
+  if (mp->direction == FLOWPROBE_DIRECTION_RX)
+    direction = FLOW_DIRECTION_RX;
+  else if (mp->direction == FLOWPROBE_DIRECTION_TX)
+    direction = FLOW_DIRECTION_TX;
+  else if (mp->direction == FLOWPROBE_DIRECTION_BOTH)
+    direction = FLOW_DIRECTION_BOTH;
+  else
+    {
+      clib_warning ("Invalid value of direction");
+      rv = VNET_API_ERROR_INVALID_VALUE;
+      goto out;
+    }
+
+  if (fm->record == 0)
+    {
+      clib_warning ("Please specify flowprobe params record first");
+      rv = VNET_API_ERROR_CANNOT_ENABLE_DISABLE_FEATURE;
+      goto out;
+    }
 
-  s = format (0, "SCRIPT: flowprobe_tx_interface_add_del ");
-  s = format (s, "sw_if_index %d is_add %d which %d ",
-             clib_host_to_net_u32 (mp->sw_if_index),
-             (int) mp->is_add, (int) mp->which);
-  FINISH;
+  rv = validate_feature_on_interface (fm, sw_if_index, which);
+  if (rv == 1)
+    {
+      if (is_add)
+       {
+         clib_warning ("Variant is already enabled for given interface");
+         rv = VNET_API_ERROR_ENTRY_ALREADY_EXISTS;
+         goto out;
+       }
+    }
+  else if (rv == 0)
+    {
+      clib_warning ("Interface has different variant enabled");
+      rv = VNET_API_ERROR_ENTRY_ALREADY_EXISTS;
+      goto out;
+    }
+  else if (rv == -1)
+    {
+      if (!is_add)
+       {
+         clib_warning ("Interface has no variant enabled");
+         rv = VNET_API_ERROR_NO_SUCH_ENTRY;
+         goto out;
+       }
+    }
+
+  rv = flowprobe_interface_add_del_feature (fm, sw_if_index, which, direction,
+                                           is_add);
+
+out:
+  BAD_SW_IF_INDEX_LABEL;
+
+  REPLY_MACRO (VL_API_FLOWPROBE_INTERFACE_ADD_DEL_REPLY);
 }
 
 #define vec_neg_search(v,E)         \
@@ -730,7 +822,10 @@ vl_api_flowprobe_params_t_handler (vl_api_flowprobe_params_t * mp)
   int rv = 0;
 
   rv = flowprobe_params
-    (fm, mp->record_l2, mp->record_l3, mp->record_l4,
+    (fm,
+     mp->record_flags & FLOWPROBE_RECORD_FLAG_L2,
+     mp->record_flags & FLOWPROBE_RECORD_FLAG_L3,
+     mp->record_flags & FLOWPROBE_RECORD_FLAG_L4,
      clib_net_to_host_u32 (mp->active_timer),
      clib_net_to_host_u32 (mp->passive_timer));
 
@@ -744,10 +839,25 @@ VLIB_PLUGIN_REGISTER () = {
 };
 /* *INDENT-ON* */
 
+u8 *
+format_flowprobe_direction (u8 *s, va_list *args)
+{
+  u8 *direction = va_arg (*args, u8 *);
+  if (*direction == FLOW_DIRECTION_RX)
+    s = format (s, "rx");
+  else if (*direction == FLOW_DIRECTION_TX)
+    s = format (s, "tx");
+  else if (*direction == FLOW_DIRECTION_BOTH)
+    s = format (s, "rx tx");
+
+  return s;
+}
+
 u8 *
 format_flowprobe_entry (u8 * s, va_list * args)
 {
   flowprobe_entry_t *e = va_arg (*args, flowprobe_entry_t *);
+  s = format (s, " %U", format_flowprobe_direction, &e->key.direction);
   s = format (s, " %d/%d", e->key.rx_sw_if_index, e->key.tx_sw_if_index);
 
   s = format (s, " %U %U", format_ethernet_address, &e->key.src_mac,
@@ -762,6 +872,43 @@ format_flowprobe_entry (u8 * s, va_list * args)
   return s;
 }
 
+u8 *
+format_flowprobe_feature (u8 * s, va_list * args)
+{
+  u8 *which = va_arg (*args, u8 *);
+  if (*which == FLOW_VARIANT_IP4)
+    s = format (s, "ip4");
+  else if (*which == FLOW_VARIANT_IP6)
+    s = format (s, "ip6");
+  else if (*which == FLOW_VARIANT_L2)
+    s = format (s, "l2");
+
+  return s;
+}
+
+u8 *
+format_flowprobe_params (u8 * s, va_list * args)
+{
+  flowprobe_record_t flags = va_arg (*args, flowprobe_record_t);
+  u32 active_timer = va_arg (*args, u32);
+  u32 passive_timer = va_arg (*args, u32);
+
+  if (flags & FLOW_RECORD_L2)
+    s = format (s, " l2");
+  if (flags & FLOW_RECORD_L3)
+    s = format (s, " l3");
+  if (flags & FLOW_RECORD_L4)
+    s = format (s, " l4");
+
+  if (active_timer != (u32) ~ 0)
+    s = format (s, " active: %d", active_timer);
+
+  if (passive_timer != (u32) ~ 0)
+    s = format (s, " passive: %d", passive_timer);
+
+  return s;
+}
+
 static clib_error_t *
 flowprobe_show_table_fn (vlib_main_t * vm,
                         unformat_input_t * input, vlib_cli_command_t * cm)
@@ -775,12 +922,12 @@ flowprobe_show_table_fn (vlib_main_t * vm,
   for (i = 0; i < vec_len (fm->pool_per_worker); i++)
     {
       /* *INDENT-OFF* */
-      pool_foreach (e, fm->pool_per_worker[i], (
+      pool_foreach (e, fm->pool_per_worker[i])
        {
          vlib_cli_output (vm, "%U",
                           format_flowprobe_entry,
                           e);
-       }));
+       }
       /* *INDENT-ON* */
 
     }
@@ -807,14 +954,15 @@ flowprobe_show_stats_fn (vlib_main_t * vm,
 }
 
 static clib_error_t *
-flowprobe_tx_interface_add_del_feature_command_fn (vlib_main_t * vm,
-                                                  unformat_input_t * input,
-                                                  vlib_cli_command_t * cmd)
+flowprobe_interface_add_del_feature_command_fn (vlib_main_t *vm,
+                                               unformat_input_t *input,
+                                               vlib_cli_command_t *cmd)
 {
   flowprobe_main_t *fm = &flowprobe_main;
   u32 sw_if_index = ~0;
   int is_add = 1;
   u8 which = FLOW_VARIANT_IP4;
+  flowprobe_direction_t direction = FLOW_DIRECTION_TX;
   int rv;
 
   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
@@ -829,6 +977,12 @@ flowprobe_tx_interface_add_del_feature_command_fn (vlib_main_t * vm,
        which = FLOW_VARIANT_IP6;
       else if (unformat (input, "l2"))
        which = FLOW_VARIANT_L2;
+      else if (unformat (input, "rx"))
+       direction = FLOW_DIRECTION_RX;
+      else if (unformat (input, "tx"))
+       direction = FLOW_DIRECTION_TX;
+      else if (unformat (input, "both"))
+       direction = FLOW_DIRECTION_BOTH;
       else
        break;
     }
@@ -850,9 +1004,16 @@ flowprobe_tx_interface_add_del_feature_command_fn (vlib_main_t * vm,
   else if (rv == 0)
     return clib_error_return (0,
                              "Interface has enable different datapath ...");
+  else if (rv == -1)
+    {
+      if (!is_add)
+       {
+         return clib_error_return (0, "Interface has no datapath enabled");
+       }
+    }
 
-  rv =
-    flowprobe_tx_interface_add_del_feature (fm, sw_if_index, which, is_add);
+  rv = flowprobe_interface_add_del_feature (fm, sw_if_index, which, direction,
+                                           is_add);
   switch (rv)
     {
     case 0:
@@ -874,6 +1035,29 @@ flowprobe_tx_interface_add_del_feature_command_fn (vlib_main_t * vm,
   return 0;
 }
 
+static clib_error_t *
+flowprobe_show_feature_command_fn (vlib_main_t * vm,
+                                  unformat_input_t * input,
+                                  vlib_cli_command_t * cmd)
+{
+  flowprobe_main_t *fm = &flowprobe_main;
+  u8 *which;
+  u32 sw_if_index;
+
+  vec_foreach (which, fm->flow_per_interface)
+  {
+    if (*which == (u8) ~ 0)
+      continue;
+
+    sw_if_index = which - fm->flow_per_interface;
+    vlib_cli_output (vm, " %U %U %U", format_vnet_sw_if_index_name,
+                    vnet_get_main (), sw_if_index, format_flowprobe_feature,
+                    which, format_flowprobe_direction,
+                    &fm->direction_per_interface[sw_if_index]);
+  }
+  return 0;
+}
+
 static clib_error_t *
 flowprobe_params_command_fn (vlib_main_t * vm,
                             unformat_input_t * input,
@@ -917,6 +1101,21 @@ flowprobe_params_command_fn (vlib_main_t * vm,
   return 0;
 }
 
+static clib_error_t *
+flowprobe_show_params_command_fn (vlib_main_t * vm,
+                                 unformat_input_t * input,
+                                 vlib_cli_command_t * cmd)
+{
+  flowprobe_main_t *fm = &flowprobe_main;
+  flowprobe_record_t flags = fm->record;
+  u32 active_timer = fm->active_timer;
+  u32 passive_timer = fm->passive_timer;
+
+  vlib_cli_output (vm, "%U", format_flowprobe_params, flags, active_timer,
+                  passive_timer);
+  return 0;
+}
+
 /*?
  * '<em>flowprobe feature add-del</em>' commands to enable/disable
  * per-packet IPFIX flow record generation on an interface
@@ -933,10 +1132,10 @@ flowprobe_params_command_fn (vlib_main_t * vm,
 ?*/
 /* *INDENT-OFF* */
 VLIB_CLI_COMMAND (flowprobe_enable_disable_command, static) = {
-    .path = "flowprobe feature add-del",
-    .short_help =
-    "flowprobe feature add-del <interface-name> <l2|ip4|ip6> disable",
-    .function = flowprobe_tx_interface_add_del_feature_command_fn,
+  .path = "flowprobe feature add-del",
+  .short_help = "flowprobe feature add-del <interface-name> [(l2|ip4|ip6)] "
+               "[(rx|tx|both)] [disable]",
+  .function = flowprobe_interface_add_del_feature_command_fn,
 };
 VLIB_CLI_COMMAND (flowprobe_params_command, static) = {
     .path = "flowprobe params",
@@ -944,6 +1143,19 @@ VLIB_CLI_COMMAND (flowprobe_params_command, static) = {
     "flowprobe params record <[l2] [l3] [l4]> [active <timer> passive <timer>]",
     .function = flowprobe_params_command_fn,
 };
+
+VLIB_CLI_COMMAND (flowprobe_show_feature_command, static) = {
+    .path = "show flowprobe feature",
+    .short_help =
+    "show flowprobe feature",
+    .function = flowprobe_show_feature_command_fn,
+};
+VLIB_CLI_COMMAND (flowprobe_show_params_command, static) = {
+    .path = "show flowprobe params",
+    .short_help =
+    "show flowprobe params",
+    .function = flowprobe_show_params_command_fn,
+};
 VLIB_CLI_COMMAND (flowprobe_show_table_command, static) = {
     .path = "show flowprobe table",
     .short_help = "show flowprobe table",
@@ -975,13 +1187,13 @@ timer_process (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f)
   vec_reset_length (event_data);
 
   int i;
-  if (vec_len (vlib_mains) == 0)
+  if (vlib_get_n_threads () == 0)
     vec_add1 (worker_vms, vm);
   else
     {
-      for (i = 0; i < vec_len (vlib_mains); i++)
+      for (i = 0; i < vlib_get_n_threads (); i++)
        {
-         worker_vm = vlib_mains[i];
+         worker_vm = vlib_get_main_by_index (i);
          if (worker_vm)
            vec_add1 (worker_vms, worker_vm);
        }